mirror of https://github.com/koreader/koreader
file reorganisation
all lua frontend files are now in the frontend/ directory. all old code is cleaned up.pull/2/merge
parent
2a2a0828c8
commit
9e531fc2db
@ -1,187 +0,0 @@
|
||||
require "keys"
|
||||
|
||||
Keydef = {
|
||||
keycode = nil,
|
||||
modifier = nil,
|
||||
descr = nil
|
||||
}
|
||||
|
||||
function Keydef:_new(obj)
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
setmetatable(obj, self)
|
||||
self.__index = self
|
||||
self.__tostring=Keydef.tostring
|
||||
return obj
|
||||
end
|
||||
|
||||
function Keydef:new(keycode,modifier,descr)
|
||||
obj = Keydef:_new()
|
||||
obj.keycode = keycode
|
||||
obj.modifier = modifier
|
||||
obj.descr = descr
|
||||
return obj
|
||||
end
|
||||
|
||||
function Keydef:display()
|
||||
return (self.modifier or "")..(self.descr or "")
|
||||
end
|
||||
|
||||
function Keydef:tostring()
|
||||
return ((self.modifier and self.modifier.."+") or "").."["..(self.keycode or "").."]"..(self.descr or "")
|
||||
end
|
||||
|
||||
|
||||
Command = {
|
||||
keydef = nil,
|
||||
keygroup = nil,
|
||||
func = nil,
|
||||
help = nil,
|
||||
order = nil
|
||||
}
|
||||
|
||||
function Command:_new(obj)
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
setmetatable(obj, self)
|
||||
self.__index = self
|
||||
self.__tostring=Command.tostring
|
||||
return obj
|
||||
end
|
||||
|
||||
function Command:new(keydef, func, help, keygroup, order)
|
||||
obj = Command:_new()
|
||||
obj.keydef = keydef
|
||||
obj.func = func
|
||||
obj.help = help
|
||||
obj.keygroup = keygroup
|
||||
obj.order = order
|
||||
--debug("creating command: ["..tostring(keydef).."] keygroup:["..(keygroup or "").."] help:"..help)
|
||||
return obj
|
||||
end
|
||||
|
||||
function Command:tostring()
|
||||
return tostring(self.keydef)..": "..(self.help or "<no help defined>")
|
||||
end
|
||||
|
||||
|
||||
Commands = {
|
||||
map = {},
|
||||
size = 0
|
||||
}
|
||||
|
||||
function Commands:add(keycode,modifier,keydescr,help,func)
|
||||
if type(keycode) == "table" then
|
||||
for i=1,#keycode,1 do
|
||||
local keydef = Keydef:new(keycode[i],modifier,keydescr)
|
||||
self:_addImpl(keydef,help,func)
|
||||
end
|
||||
else
|
||||
local keydef = Keydef:new(keycode,modifier,keydescr)
|
||||
self:_addImpl(keydef,help,func)
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:addGroup(keygroup,keys,help,func)
|
||||
for _k,keydef in pairs(keys) do
|
||||
self:_addImpl(keydef,help,func,keygroup)
|
||||
end
|
||||
end
|
||||
|
||||
--@TODO handle MOD_ANY 06.04 2012 (houqp)
|
||||
function Commands:del(keycode, modifier, keydescr)
|
||||
local keydef = nil
|
||||
|
||||
if not keydescr then
|
||||
for k,v in pairs(self.map) do
|
||||
if v.keydef.keycode == keycode
|
||||
and v.keydef.modifier == modifier then
|
||||
keydef = k
|
||||
break
|
||||
end
|
||||
end -- EOF for
|
||||
else
|
||||
keydef = Keydef:new(keycode, modifier, keydescr)
|
||||
end -- EOF if
|
||||
|
||||
self.map[keydef] = nil
|
||||
end
|
||||
|
||||
function Commands:delGroup(keygroup)
|
||||
if keygroup then
|
||||
for k,v in pairs(self.map) do
|
||||
if v.keygroup == keygroup then
|
||||
self.map[k] = nil
|
||||
end
|
||||
end -- EOF for
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:_addImpl(keydef,help,func,keygroup)
|
||||
if keydef.modifier==MOD_ANY then
|
||||
self:addGroup(keygroup or keydef.descr,{Keydef:new(keydef.keycode,nil), Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
|
||||
elseif keydef.modifier==MOD_SHIFT_OR_ALT then
|
||||
self:addGroup(keygroup or (MOD_SHIFT..MOD_ALT..(keydef.descr or "")),{Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
|
||||
else
|
||||
local command = self.map[keydef]
|
||||
if command == nil then
|
||||
self.size = self.size + 1
|
||||
command = Command:new(keydef,func,help,keygroup,self.size)
|
||||
self.map[keydef] = command
|
||||
else
|
||||
command.func = func
|
||||
command.help = help
|
||||
command.keygroup = keygroup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Commands:get(keycode,modifier)
|
||||
return self.map[Keydef:new(keycode, modifier)]
|
||||
end
|
||||
|
||||
function Commands:getByKeydef(keydef)
|
||||
return self.map[keydef]
|
||||
end
|
||||
|
||||
function Commands:new(obj)
|
||||
-- obj definition
|
||||
obj = obj or {}
|
||||
obj.map = {}
|
||||
obj.size = 0
|
||||
setmetatable(obj, self)
|
||||
self.__index = self
|
||||
|
||||
-- payload
|
||||
local mt = {}
|
||||
mt.__index = function(table, key)
|
||||
return rawget(table,(key.modifier or "").."@#@"..(key.keycode or ""))
|
||||
end
|
||||
mt.__newindex = function(table, key, value)
|
||||
return rawset(table,(key.modifier or "").."@#@"..(key.keycode or ""),value)
|
||||
end
|
||||
setmetatable(obj.map, mt)
|
||||
|
||||
obj:add(KEY_INTO_SCREEN_SAVER, nil, "Slider",
|
||||
"toggle screen saver",
|
||||
function()
|
||||
Screen:saveCurrentBB()
|
||||
InfoMessage:show("Going into screensaver... ", 0)
|
||||
Screen.kpv_rotation_mode = Screen.cur_rotation_mode
|
||||
fb:setOrientation(Screen.native_rotation_mode)
|
||||
util.sleep(1)
|
||||
os.execute("killall -cont cvm")
|
||||
end
|
||||
)
|
||||
obj:add(KEY_OUTOF_SCREEN_SAVER, nil, "Slider",
|
||||
"toggle screen saver",
|
||||
function()
|
||||
util.usleep(1500000)
|
||||
os.execute("killall -stop cvm")
|
||||
fb:setOrientation(Screen.kpv_rotation_mode)
|
||||
Screen:restoreFromSavedBB()
|
||||
fb:refresh(0)
|
||||
end
|
||||
)
|
||||
return obj
|
||||
end
|
@ -1,456 +0,0 @@
|
||||
require "font"
|
||||
require "unireader"
|
||||
require "inputbox"
|
||||
require "selectmenu"
|
||||
|
||||
CREReader = UniReader:new{
|
||||
pos = nil,
|
||||
percent = 0,
|
||||
|
||||
gamma_index = 15,
|
||||
font_face = nil,
|
||||
|
||||
line_space_percent = 100,
|
||||
}
|
||||
|
||||
function CREReader:init()
|
||||
self:addAllCommands()
|
||||
self:adjustCreReaderCommands()
|
||||
-- we need to initialize the CRE font list
|
||||
local fonts = Font:getFontList()
|
||||
for _k, _v in ipairs(fonts) do
|
||||
local ok, err = pcall(cre.registerFont, Font.fontdir..'/'.._v)
|
||||
if not ok then
|
||||
debug(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- open a CREngine supported file and its settings store
|
||||
function CREReader:open(filename)
|
||||
local ok
|
||||
local file_type = string.lower(string.match(filename, ".+%.([^.]+)"))
|
||||
-- these two format use the same css file
|
||||
if file_type == "html" then
|
||||
file_type = "htm"
|
||||
end
|
||||
local style_sheet = "./data/"..file_type..".css"
|
||||
ok, self.doc = pcall(cre.openDocument, filename, style_sheet,
|
||||
G_width, G_height)
|
||||
if not ok then
|
||||
return false, self.doc -- will contain error message
|
||||
end
|
||||
|
||||
self.doc:setDefaultInterlineSpace(self.line_space_percent)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- setting related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:loadSpecialSettings()
|
||||
local font_face = self.settings:readSetting("font_face")
|
||||
self.font_face = font_face or "FreeSerif"
|
||||
self.doc:setFontFace(self.font_face)
|
||||
|
||||
local gamma_index = self.settings:readSetting("gamma_index")
|
||||
self.gamma_index = gamma_index or self.gamma_index
|
||||
cre.setGammaIndex(self.gamma_index)
|
||||
|
||||
local line_space_percent = self.settings:readSetting("line_space_percent")
|
||||
self.line_space_percent = line_space_percent or self.line_space_percent
|
||||
end
|
||||
|
||||
function CREReader:getLastPageOrPos()
|
||||
local last_percent = self.settings:readSetting("last_percent")
|
||||
if last_percent then
|
||||
return math.floor((last_percent * self.doc:getFullHeight()) / 10000)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function CREReader:saveSpecialSettings()
|
||||
self.settings:saveSetting("font_face", self.font_face)
|
||||
self.settings:saveSetting("gamma_index", self.gamma_index)
|
||||
self.settings:saveSetting("line_space_percent", self.line_space_percent)
|
||||
end
|
||||
|
||||
function CREReader:saveLastPageOrPos()
|
||||
self.settings:saveSetting("last_percent", self.percent)
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- render related methods
|
||||
----------------------------------------------------
|
||||
-- we don't need setzoom in CREReader
|
||||
function CREReader:setzoom(page, preCache)
|
||||
return
|
||||
end
|
||||
|
||||
function CREReader:redrawCurrentPage()
|
||||
self:goto(self.pos)
|
||||
end
|
||||
|
||||
-- there is no zoom mode in CREReader
|
||||
function CREReader:setGlobalZoomMode()
|
||||
return
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- goto related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:goto(pos, is_ignore_jump, pos_type)
|
||||
local prev_xpointer = self.doc:getXPointer()
|
||||
local width, height = G_width, G_height
|
||||
|
||||
if pos_type == "xpointer" then
|
||||
self.doc:gotoXPointer(pos)
|
||||
pos = self.doc:getCurrentPos()
|
||||
else -- pos_type is position within document
|
||||
pos = math.min(pos, self.doc:getFullHeight() - height)
|
||||
pos = math.max(pos, 0)
|
||||
self.doc:gotoPos(pos)
|
||||
end
|
||||
|
||||
-- add to jump history, distinguish jump from normal page turn
|
||||
-- NOTE:
|
||||
-- even though we have called gotoPos() or gotoXPointer() previously,
|
||||
-- self.pos hasn't been updated yet here, so we can still make use of it.
|
||||
if not is_ignore_jump then
|
||||
if self.pos and math.abs(self.pos - pos) > height then
|
||||
self:addJump(prev_xpointer)
|
||||
end
|
||||
end
|
||||
|
||||
self.doc:drawCurrentPage(self.nulldc, fb.bb)
|
||||
|
||||
debug("## self.show_overlap "..self.show_overlap)
|
||||
if self.show_overlap < 0 then
|
||||
fb.bb:dimRect(0,0, width, -self.show_overlap)
|
||||
elseif self.show_overlap > 0 then
|
||||
fb.bb:dimRect(0,height - self.show_overlap, width, self.show_overlap)
|
||||
end
|
||||
self.show_overlap = 0
|
||||
|
||||
if self.rcount >= self.rcountmax then
|
||||
debug("full refresh")
|
||||
self.rcount = 0
|
||||
fb:refresh(0)
|
||||
else
|
||||
debug("partial refresh")
|
||||
self.rcount = self.rcount + 1
|
||||
fb:refresh(1)
|
||||
end
|
||||
|
||||
self.pos = pos
|
||||
self.pageno = self.doc:getCurrentPage()
|
||||
self.percent = self.doc:getCurrentPercent()
|
||||
end
|
||||
|
||||
function CREReader:gotoPercent(percent)
|
||||
self:goto(percent * self.doc:getFullHeight() / 10000)
|
||||
end
|
||||
|
||||
function CREReader:gotoTocEntry(entry)
|
||||
self:goto(entry.xpointer, nil, "xpointer")
|
||||
end
|
||||
|
||||
function CREReader:nextView()
|
||||
self.show_overlap = -self.pan_overlap_vertical
|
||||
return self.pos + G_height - self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
function CREReader:prevView()
|
||||
self.show_overlap = self.pan_overlap_vertical
|
||||
return self.pos - G_height + self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- jump history related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:isSamePage(p1, p2)
|
||||
return self.doc:getPageFromXPointer(p1) == self.doc:getPageFromXPointer(p2)
|
||||
end
|
||||
|
||||
function CREReader:showJumpHist()
|
||||
local menu_items = {}
|
||||
for k,v in ipairs(self.jump_history) do
|
||||
if k == self.jump_history.cur then
|
||||
cur_sign = "*(Cur) "
|
||||
else
|
||||
cur_sign = ""
|
||||
end
|
||||
table.insert(menu_items,
|
||||
cur_sign..v.datetime.." -> Page "
|
||||
..self.doc:getPageFromXPointer(v.page).." "..v.notes)
|
||||
end
|
||||
|
||||
if #menu_items == 0 then
|
||||
showInfoMsgWithDelay(
|
||||
"No jump history found.", 2000, 1)
|
||||
else
|
||||
-- if cur points to head, draw entry for current page
|
||||
if self.jump_history.cur > #self.jump_history then
|
||||
table.insert(menu_items,
|
||||
"Current Page "..self.pageno)
|
||||
end
|
||||
|
||||
jump_menu = SelectMenu:new{
|
||||
menu_title = "Jump History",
|
||||
item_array = menu_items,
|
||||
}
|
||||
item_no = jump_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no and item_no <= #self.jump_history then
|
||||
local jump_item = self.jump_history[item_no]
|
||||
self.jump_history.cur = item_no
|
||||
self:goto(jump_item.page, true, "xpointer")
|
||||
else
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- bookmarks related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:showBookMarks()
|
||||
local menu_items = {}
|
||||
-- build menu items
|
||||
for k,v in ipairs(self.bookmarks) do
|
||||
table.insert(menu_items,
|
||||
"Page "..self.doc:getPageFromXPointer(v.page)
|
||||
.." "..v.notes.." @ "..v.datetime)
|
||||
end
|
||||
if #menu_items == 0 then
|
||||
showInfoMsgWithDelay(
|
||||
"No bookmark found.", 2000, 1)
|
||||
else
|
||||
toc_menu = SelectMenu:new{
|
||||
menu_title = "Bookmarks",
|
||||
item_array = menu_items,
|
||||
}
|
||||
item_no = toc_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no then
|
||||
self:goto(self.bookmarks[item_no].page, nil, "xpointer")
|
||||
else
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- TOC related methods
|
||||
----------------------------------------------------
|
||||
function CREReader:getTocTitleByPage(page_or_xpoint)
|
||||
local page = 1
|
||||
-- tranform xpointer to page
|
||||
if type(page_or_xpoint) == "string" then
|
||||
page = self.doc:getPageFromXPointer(page_or_xpoint)
|
||||
else
|
||||
page = page_or_xpoint
|
||||
end
|
||||
return self:_getTocTitleByPage(page)
|
||||
end
|
||||
|
||||
function CREReader:getTocTitleOfCurrentPage()
|
||||
return self:getTocTitleByPage(self.doc:getXPointer())
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- menu related methods
|
||||
----------------------------------------------------
|
||||
-- used in CREReader:showMenu()
|
||||
function CREReader:_drawReadingInfo()
|
||||
local ypos = G_height - 50
|
||||
local load_percent = self.percent/100
|
||||
|
||||
fb.bb:paintRect(0, ypos, G_width, 50, 0)
|
||||
|
||||
ypos = ypos + 15
|
||||
local face = Font:getFace("rifont", 22)
|
||||
local cur_section = self:getTocTitleOfCurrentPage()
|
||||
if cur_section ~= "" then
|
||||
cur_section = "Section: "..cur_section
|
||||
end
|
||||
renderUtf8Text(fb.bb, 10, ypos+6, face,
|
||||
"Position: "..load_percent.."%".." "..cur_section, true)
|
||||
|
||||
ypos = ypos + 15
|
||||
blitbuffer.progressBar(fb.bb, 10, ypos, G_width - 20, 15,
|
||||
5, 4, load_percent/100, 8)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function CREReader:adjustCreReaderCommands()
|
||||
-- delete commands
|
||||
self.commands:delGroup("[joypad]")
|
||||
self.commands:del(KEY_G, nil, "G")
|
||||
self.commands:del(KEY_J, MOD_SHIFT, "J")
|
||||
self.commands:del(KEY_K, MOD_SHIFT, "K")
|
||||
self.commands:del(KEY_Z, nil, "Z")
|
||||
self.commands:del(KEY_Z, MOD_SHIFT, "Z")
|
||||
self.commands:del(KEY_Z, MOD_ALT, "Z")
|
||||
self.commands:del(KEY_A, nil, "A")
|
||||
self.commands:del(KEY_A, MOD_SHIFT, "A")
|
||||
self.commands:del(KEY_A, MOD_ALT, "A")
|
||||
self.commands:del(KEY_S, nil, "S")
|
||||
self.commands:del(KEY_S, MOD_SHIFT, "S")
|
||||
self.commands:del(KEY_S, MOD_ALT, "S")
|
||||
self.commands:del(KEY_D, nil, "D")
|
||||
self.commands:del(KEY_D, MOD_SHIFT, "D")
|
||||
self.commands:del(KEY_D, MOD_ALT, "D")
|
||||
self.commands:del(KEY_X, nil, "X")
|
||||
self.commands:del(KEY_F, MOD_SHIFT, "F")
|
||||
self.commands:del(KEY_F, MOD_ALT, "F")
|
||||
self.commands:del(KEY_N, nil, "N") -- highlight
|
||||
self.commands:del(KEY_N, MOD_SHIFT, "N") -- show highlights
|
||||
|
||||
-- overwrite commands
|
||||
|
||||
self.commands:addGroup(MOD_SHIFT.."< >",{
|
||||
Keydef:new(KEY_PGBCK,MOD_SHIFT),Keydef:new(KEY_PGFWD,MOD_SHIFT),
|
||||
Keydef:new(KEY_LPGBCK,MOD_SHIFT),Keydef:new(KEY_LPGFWD,MOD_SHIFT)},
|
||||
"increase/decrease font size",
|
||||
function(self)
|
||||
local delta = 1
|
||||
local change = "increase"
|
||||
if keydef.keycode == KEY_PGBCK or keydef.keycode == KEY_LPGBCK then
|
||||
delta = -1
|
||||
change = "decrease"
|
||||
end
|
||||
InfoMessage:show(change.." font size", 0)
|
||||
self.doc:zoomFont(delta)
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:addGroup(MOD_ALT.."< >",{
|
||||
Keydef:new(KEY_PGBCK,MOD_ALT),Keydef:new(KEY_PGFWD,MOD_ALT),
|
||||
Keydef:new(KEY_LPGBCK,MOD_ALT),Keydef:new(KEY_LPGFWD,MOD_ALT)},
|
||||
"increase/decrease line spacing",
|
||||
function(self)
|
||||
if keydef.keycode == KEY_PGBCK or keydef.keycode == KEY_LPGBCK then
|
||||
self.line_space_percent = self.line_space_percent - 10
|
||||
if self.line_space_percent < 100 then
|
||||
self.line_space_percent = 100
|
||||
end
|
||||
else
|
||||
self.line_space_percent = self.line_space_percent + 10
|
||||
if self.line_space_percent > 200 then
|
||||
self.line_space_percent = 200
|
||||
end
|
||||
end
|
||||
InfoMessage:show("line spacing "..self.line_space_percent.."%", 0)
|
||||
debug("line spacing set to", self.line_space_percent)
|
||||
self.doc:setDefaultInterlineSpace(self.line_space_percent)
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
local numeric_keydefs = {}
|
||||
for i=1,10 do
|
||||
numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10))
|
||||
end
|
||||
self.commands:addGroup("[1..0]", numeric_keydefs,
|
||||
"jump to <key>*10% of document",
|
||||
function(self, keydef)
|
||||
debug('jump to position: '..
|
||||
math.floor(self.doc:getFullHeight()*(keydef.keycode-KEY_1)/9)..
|
||||
'/'..self.doc:getFullHeight())
|
||||
self:goto(math.floor(self.doc:getFullHeight()*(keydef.keycode-KEY_1)/9))
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_F, nil, "F",
|
||||
"change document font",
|
||||
function(self)
|
||||
Screen:saveCurrentBB()
|
||||
|
||||
local face_list = cre.getFontFaces()
|
||||
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = face_list,
|
||||
}
|
||||
|
||||
local item_no = fonts_menu:choose(0, G_height)
|
||||
debug(face_list[item_no])
|
||||
if item_no then
|
||||
Screen:restoreFromSavedBB()
|
||||
self.doc:setFontFace(face_list[item_no])
|
||||
self.font_face = face_list[item_no]
|
||||
InfoMessage:show("Redrawing with "..face_list[item_no], 0)
|
||||
end
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_F, MOD_ALT, "F",
|
||||
"Toggle font bolder attribute",
|
||||
function(self)
|
||||
self.doc:toggleFontBolder()
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_B, MOD_ALT, "B",
|
||||
"add bookmark to current page",
|
||||
function(self)
|
||||
ok = self:addBookmark(self.doc:getXPointer())
|
||||
if not ok then
|
||||
showInfoMsgWithDelay("Page already marked!", 2000, 1)
|
||||
else
|
||||
showInfoMsgWithDelay("Page marked.", 2000, 1)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, nil, "Back",
|
||||
"go backward in jump history",
|
||||
function(self)
|
||||
local prev_jump_no = self.jump_history.cur - 1
|
||||
if prev_jump_no >= 1 then
|
||||
self.jump_history.cur = prev_jump_no
|
||||
self:goto(self.jump_history[prev_jump_no].page, true, "xpointer")
|
||||
else
|
||||
showInfoMsgWithDelay("Already first jump!", 2000, 1)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, MOD_SHIFT, "Back",
|
||||
"go forward in jump history",
|
||||
function(self)
|
||||
local next_jump_no = self.jump_history.cur + 1
|
||||
if next_jump_no <= #self.jump_history then
|
||||
self.jump_history.cur = next_jump_no
|
||||
self:goto(self.jump_history[next_jump_no].page, true, "xpointer")
|
||||
else
|
||||
showInfoMsgWithDelay("Already last jump!", 2000, 1)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:addGroup("vol-/+",
|
||||
{Keydef:new(KEY_VPLUS,nil), Keydef:new(KEY_VMINUS,nil)},
|
||||
"decrease/increase gamma",
|
||||
function(self, keydef)
|
||||
local delta = 1
|
||||
if keydef.keycode == KEY_VMINUS then
|
||||
delta = -1
|
||||
end
|
||||
cre.setGammaIndex(self.gamma_index+delta)
|
||||
self.gamma_index = cre.getGammaIndex()
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"pan "..self.shift_y.." pixels upwards",
|
||||
function(self)
|
||||
self:goto(self.pos - self.shift_y)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"pan "..self.shift_y.." pixels downwards",
|
||||
function(self)
|
||||
self:goto(self.pos + self.shift_y)
|
||||
end
|
||||
)
|
||||
end
|
@ -1,98 +0,0 @@
|
||||
require "unireader"
|
||||
|
||||
DJVUReader = UniReader:new{}
|
||||
|
||||
-- open a DJVU file and its settings store
|
||||
-- DJVU does not support password yet
|
||||
function DJVUReader:open(filename)
|
||||
local ok
|
||||
ok, self.doc = pcall(djvu.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return ok, self.doc -- this will be the error message instead
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
function DJVUReader:init()
|
||||
self:addAllCommands()
|
||||
self:adjustDjvuReaderCommand()
|
||||
end
|
||||
|
||||
function DJVUReader:adjustDjvuReaderCommand()
|
||||
self.commands:del(KEY_J, MOD_SHIFT, "J")
|
||||
self.commands:del(KEY_K, MOD_SHIFT, "K")
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- highlight support
|
||||
----------------------------------------------------
|
||||
function DJVUReader:getText(pageno)
|
||||
return self.doc:getPageText(pageno)
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- In djvulibre library, some coordinates starts from
|
||||
-- lower left conner, i.e. y is upside down in kpv's
|
||||
-- coordinate. So y0 should be taken with special care.
|
||||
----------------------------------------------------
|
||||
function DJVUReader:zoomedRectCoordTransform(x0, y0, x1, y1)
|
||||
local x,y = self:screenOffset()
|
||||
return
|
||||
x0 * self.globalzoom + x,
|
||||
self.cur_full_height - (y1 * self.globalzoom) + y,
|
||||
(x1 - x0) * self.globalzoom,
|
||||
(y1 - y0) * self.globalzoom
|
||||
end
|
||||
|
||||
-- make sure at least part of the box can be seen in next/previous view
|
||||
-- @FIXME only works in FIT_TO_CONTENT_WIDTH mode 21.04 2012 (houqp)
|
||||
-- @TODO use zoomedRectCoordTransform in unireader, no need to overwrite
|
||||
-- it in here.
|
||||
function DJVUReader:_isBoxInNextView(box)
|
||||
return self.cur_full_height - (box.y0 * self.globalzoom) > -self.offset_y + G_height
|
||||
end
|
||||
|
||||
function DJVUReader:_isBoxInPrevView(box)
|
||||
return self.cur_full_height - (box.y1 * self.globalzoom) < -self.offset_y
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isEntireWordInScreenHeightRange(w)
|
||||
return (w ~= nil) and
|
||||
(self.cur_full_height - (w.y1 * self.globalzoom) >=
|
||||
-self.offset_y) and
|
||||
(self.cur_full_height - (w.y0 * self.globalzoom) <=
|
||||
-self.offset_y + G_height)
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isEntireLineInScreenHeightRange(l)
|
||||
return (l ~= nil) and
|
||||
(self.cur_full_height - (l.y1 * self.globalzoom) >=
|
||||
-self.offset_y) and
|
||||
(self.cur_full_height - (l.y0 * self.globalzoom) <=
|
||||
-self.offset_y + G_height)
|
||||
end
|
||||
|
||||
-- y axel in djvulibre starts from bottom
|
||||
function DJVUReader:_isWordInScreenRange(w)
|
||||
if not w then
|
||||
return false
|
||||
end
|
||||
|
||||
is_entire_word_out_of_screen_height =
|
||||
(self.cur_full_height - (w.y0 * self.globalzoom) <=
|
||||
-self.offset_y)
|
||||
or (self.cur_full_height - (w.y1 * self.globalzoom) >=
|
||||
-self.offset_y + G_height)
|
||||
|
||||
is_entire_word_out_of_screen_width =
|
||||
(w.x0 * self.globalzoom >= -self.offset_x + G_width
|
||||
or w.x1 * self.globalzoom <= -self.offset_x)
|
||||
|
||||
return (not is_entire_word_out_of_screen_height) and
|
||||
(not is_entire_word_out_of_screen_width)
|
||||
end
|
||||
|
||||
|
@ -1,243 +0,0 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "filesearcher"
|
||||
require "inputbox"
|
||||
require "selectmenu"
|
||||
|
||||
FileChooser = {
|
||||
-- Class vars:
|
||||
|
||||
-- spacing between lines
|
||||
spacing = 40,
|
||||
|
||||
-- state buffer
|
||||
dirs = nil,
|
||||
files = nil,
|
||||
items = 0,
|
||||
path = "",
|
||||
page = 1,
|
||||
current = 1,
|
||||
oldcurrent = 0,
|
||||
exception_message = nil
|
||||
}
|
||||
|
||||
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 not (f==".." and self.path=="/") and not string.match(f, "^%.[^.]") then
|
||||
--debug(self.path.." -> adding: '"..f.."'")
|
||||
table.insert(self.dirs, f)
|
||||
else
|
||||
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
||||
if file_type == "djvu"
|
||||
or file_type == "pdf" or file_type == "xps" or file_type == "cbz"
|
||||
or file_type == "epub" or file_type == "txt" or file_type == "rtf"
|
||||
or file_type == "htm" or file_type == "html" or file_type == "mobi"
|
||||
or file_type == "fb2" or file_type == "chm" or file_type == "doc"
|
||||
or file_type == "zip" then
|
||||
table.insert(self.files, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
--@TODO make sure .. is sortted to the first item 16.02 2012
|
||||
table.sort(self.dirs)
|
||||
table.sort(self.files)
|
||||
end
|
||||
|
||||
function FileChooser:setPath(newPath)
|
||||
local curr_path = self.path
|
||||
self.path = getAbsolutePath(newPath)
|
||||
local readdir_ok, exc = pcall(self.readDir,self)
|
||||
if(not readdir_ok) then
|
||||
debug("readDir error: "..tostring(exc))
|
||||
self.exception_message = exc
|
||||
return self:setPath(curr_path)
|
||||
else
|
||||
self.items = #self.dirs + #self.files
|
||||
if self.items == 0 then
|
||||
return nil
|
||||
end
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function FileChooser:choose(ypos, height)
|
||||
local perpage = math.floor(height / self.spacing) - 2
|
||||
local pagedirty = true
|
||||
local markerdirty = false
|
||||
|
||||
local prevItem = function ()
|
||||
if self.current == 1 then
|
||||
if self.page > 1 then
|
||||
self.current = perpage
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
self.current = self.current - 1
|
||||
markerdirty = true
|
||||
end
|
||||
end
|
||||
|
||||
local nextItem = function ()
|
||||
if self.current == perpage then
|
||||
if self.page < (self.items / perpage) then
|
||||
self.current = 1
|
||||
self.page = self.page + 1
|
||||
pagedirty = true
|
||||
end
|
||||
else
|
||||
if self.page ~= math.floor(self.items / perpage) + 1
|
||||
or self.current + (self.page-1)*perpage < self.items then
|
||||
self.current = self.current + 1
|
||||
markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local cface = Font:getFace("cfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if pagedirty then
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
local c
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= #self.dirs then
|
||||
-- resembles display in midnight commander: adds "/" prefix for directories
|
||||
renderUtf8Text(fb.bb, 39, ypos + self.spacing*c, cface, "/", true)
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, self.dirs[i], true)
|
||||
elseif i <= self.items then
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, cface, self.files[i-#self.dirs], true)
|
||||
end
|
||||
end
|
||||
all_page = math.ceil(self.items/perpage)
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * perpage + 42, fface,
|
||||
"Page "..self.page.." of "..all_page, true)
|
||||
local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or "Path: "..self.path
|
||||
self.exception_message = nil
|
||||
renderUtf8Text(fb.bb, 5, ypos + self.spacing * (perpage+1) + 27, fface, msg, true)
|
||||
markerdirty = true
|
||||
end
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
fb.bb:paintRect(30, ypos + self.spacing*self.oldcurrent + 10, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 30, ypos + self.spacing*self.oldcurrent + 10, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
end
|
||||
fb.bb:paintRect(30, ypos + self.spacing*self.current + 10, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
fb:refresh(1, 30, ypos + self.spacing*self.current + 10, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
markerdirty = false
|
||||
end
|
||||
if pagedirty then
|
||||
fb:refresh(0, 0, ypos, fb.bb:getWidth(), height)
|
||||
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
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_F then -- invoke fontchooser menu
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = Font:getFontList(),
|
||||
}
|
||||
local re, font = fonts_menu:choose(0, height)
|
||||
if re then
|
||||
Font.fontmap["cfont"] = font
|
||||
Font:update()
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_S then -- invoke search input
|
||||
keywords = InputBox:input(height-100, 100, "Search:")
|
||||
if keywords then
|
||||
-- call FileSearcher
|
||||
--[[
|
||||
This might looks a little bit dirty for using callback.
|
||||
But I cannot come up with a better solution for renewing
|
||||
the height argument according to screen rotation mode.
|
||||
|
||||
The callback might also be useful for calling system
|
||||
settings menu in the future.
|
||||
--]]
|
||||
return nil, function()
|
||||
InfoMessage:show("Searching...",0)
|
||||
FileSearcher:init( self.path )
|
||||
FileSearcher:choose(keywords)
|
||||
end
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_PGFWD or ev.code == KEY_LPGFWD then
|
||||
if self.page < (self.items / perpage) then
|
||||
if self.current + self.page*perpage > self.items then
|
||||
self.current = self.items - self.page*perpage
|
||||
end
|
||||
self.page = self.page + 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = self.items - (self.page-1)*perpage
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = 1
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
local newdir = self.dirs[perpage*(self.page-1)+self.current]
|
||||
if newdir == ".." then
|
||||
local path = string.gsub(self.path, "(.*)/[^/]+/?$", "%1")
|
||||
self:setPath(path)
|
||||
elseif newdir then
|
||||
local path = self.path.."/"..newdir
|
||||
self:setPath(path)
|
||||
else
|
||||
return self.path.."/"..self.files[perpage*(self.page-1)+self.current - #self.dirs]
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_BACK or ev.code == KEY_HOME then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,318 +0,0 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
|
||||
FileSearcher = {
|
||||
-- title height
|
||||
title_H = 45,
|
||||
-- spacing between lines
|
||||
spacing = 40,
|
||||
-- foot height
|
||||
foot_H = 27,
|
||||
|
||||
-- state buffer
|
||||
dirs = {},
|
||||
files = {},
|
||||
result = {},
|
||||
items = 0,
|
||||
page = 0,
|
||||
current = 1,
|
||||
oldcurrent = 1,
|
||||
}
|
||||
|
||||
function FileSearcher:readDir()
|
||||
self.dirs = {self.path}
|
||||
self.files = {}
|
||||
while #self.dirs ~= 0 do
|
||||
new_dirs = {}
|
||||
-- handle each dir
|
||||
for __, d in pairs(self.dirs) do
|
||||
-- handle files in d
|
||||
for f in lfs.dir(d) do
|
||||
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
||||
if lfs.attributes(d.."/"..f, "mode") == "directory"
|
||||
and f ~= "." and f~= ".." and not string.match(f, "^%.[^.]") then
|
||||
table.insert(new_dirs, d.."/"..f)
|
||||
elseif file_type == "djvu" or file_type == "pdf"
|
||||
or file_type == "xps" or file_type == "cbz"
|
||||
or file_type == "epub" or file_type == "txt"
|
||||
or file_type == "rtf" or file_type == "htm"
|
||||
or file_type == "html" or file_type == "mobi"
|
||||
or file_type == "fb2" or file_type == "chm"
|
||||
or file_type == "doc" or file_type == "zip" then
|
||||
file_entry = {dir=d, name=f,}
|
||||
table.insert(self.files, file_entry)
|
||||
--debug("file:"..d.."/"..f)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.dirs = new_dirs
|
||||
end
|
||||
end
|
||||
|
||||
function FileSearcher:setPath(newPath)
|
||||
self.path = newPath
|
||||
self:readDir()
|
||||
self.items = #self.files
|
||||
--@TODO check none found 19.02 2012
|
||||
if self.items == 0 then
|
||||
return nil
|
||||
end
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
return true
|
||||
end
|
||||
|
||||
function FileSearcher:setSearchResult(keywords)
|
||||
self.result = {}
|
||||
if keywords == " " then -- one space to show all files
|
||||
self.result = self.files
|
||||
else
|
||||
for __,f in pairs(self.files) do
|
||||
if string.find(string.lower(f.name), keywords) then
|
||||
table.insert(self.result, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.keywords = keywords
|
||||
self.items = #self.result
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
end
|
||||
|
||||
function FileSearcher:init(search_path)
|
||||
if search_path then
|
||||
self:setPath(search_path)
|
||||
else
|
||||
self:setPath("/mnt/us/documents")
|
||||
end
|
||||
self:addAllCommands()
|
||||
end
|
||||
|
||||
function FileSearcher:prevItem()
|
||||
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
|
||||
|
||||
function FileSearcher:nextItem()
|
||||
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
|
||||
|
||||
function FileSearcher:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"goto previous item",
|
||||
function(self)
|
||||
self:prevItem()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"goto next item",
|
||||
function(self)
|
||||
self:nextItem()
|
||||
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_S, nil, "S",
|
||||
"invoke search inputbox",
|
||||
function(self)
|
||||
old_keywords = self.keywords
|
||||
self.keywords = InputBox:input(G_height - 100, 100,
|
||||
"Search:", old_keywords)
|
||||
if self.keywords then
|
||||
self:setSearchResult(self.keywords)
|
||||
else
|
||||
self.keywords = old_keywords
|
||||
end
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_F, nil, "F",
|
||||
"font menu",
|
||||
function(self)
|
||||
local fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = Font:getFontList(),
|
||||
}
|
||||
local re, font = fonts_menu:choose(0, G_height)
|
||||
if re then
|
||||
Font.fontmap["cfont"] = font
|
||||
Font:update()
|
||||
end
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"select item",
|
||||
function(self)
|
||||
file_entry = self.result[self.perpage*(self.page-1)+self.current]
|
||||
file_full_path = file_entry.dir .. "/" .. file_entry.name
|
||||
|
||||
openFile(file_full_path)
|
||||
--reset height and item index if screen has been rotated
|
||||
local item_no = self.perpage * (self.page - 1) + self.current
|
||||
self.perpage = math.floor(G_height / self.spacing) - 2
|
||||
self.current = item_no % self.perpage
|
||||
self.page = math.floor(item_no / self.perpage) + 1
|
||||
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_HOME}, nil, "",
|
||||
"back to file browser",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function FileSearcher:choose(keywords)
|
||||
self.perpage = math.floor(G_height / self.spacing) - 2
|
||||
self.pagedirty = true
|
||||
self.markerdirty = false
|
||||
|
||||
|
||||
-- if given keywords, set new result according to keywords.
|
||||
-- Otherwise, display the previous search result.
|
||||
if keywords then
|
||||
self:setSearchResult(keywords)
|
||||
end
|
||||
|
||||
while true do
|
||||
local cface = Font:getFace("cfont", 22)
|
||||
local tface = Font:getFace("tfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if self.pagedirty then
|
||||
self.markerdirty = true
|
||||
fb.bb:paintRect(0, 0, G_width, G_height, 0)
|
||||
|
||||
-- draw menu title
|
||||
renderUtf8Text(fb.bb, 30, 0 + self.title_H, tface,
|
||||
"Search Result for: "..self.keywords, true)
|
||||
|
||||
-- draw results
|
||||
local c
|
||||
if self.items == 0 then -- nothing found
|
||||
y = self.title_H + self.spacing * 2
|
||||
renderUtf8Text(fb.bb, 20, y, cface,
|
||||
"Sorry, no match found.", true)
|
||||
renderUtf8Text(fb.bb, 20, y + self.spacing, cface,
|
||||
"Please try a different keyword.", true)
|
||||
self.markerdirty = false
|
||||
else -- found something, draw it
|
||||
for c = 1, self.perpage do
|
||||
local i = (self.page - 1) * self.perpage + c
|
||||
if i <= self.items then
|
||||
y = self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 50, y, cface,
|
||||
self.result[i].name, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- draw footer
|
||||
y = self.title_H + (self.spacing * self.perpage) + self.foot_H
|
||||
x = (G_width / 2) - 50
|
||||
all_page = math.ceil(self.items/self.perpage)
|
||||
renderUtf8Text(fb.bb, x, y, fface,
|
||||
"Page "..self.page.." of "..all_page, true)
|
||||
end
|
||||
|
||||
if self.markerdirty then
|
||||
if not self.pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = self.title_H + (self.spacing * self.oldcurrent) + 10
|
||||
fb.bb:paintRect(30, y, G_width - 60, 3, 0)
|
||||
fb:refresh(1, 30, y, G_width - 60, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = self.title_H + (self.spacing * self.current) + 10
|
||||
fb.bb:paintRect(30, y, G_width - 60, 3, 15)
|
||||
if not self.pagedirty then
|
||||
fb:refresh(1, 30, y, G_width - 60, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if self.pagedirty then
|
||||
fb:refresh(0)
|
||||
self.pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.saveWaitForEvent()
|
||||
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
|
||||
end -- while true
|
||||
return nil
|
||||
end
|
@ -1,6 +1,5 @@
|
||||
require "widget"
|
||||
require "font"
|
||||
require "commands"
|
||||
require "ui/widget"
|
||||
require "ui/font"
|
||||
|
||||
--[[
|
||||
Wrapper Widget that manages focus for a whole dialog
|
@ -1,3 +1,4 @@
|
||||
require "settings" -- for debug()
|
||||
|
||||
Font = {
|
||||
fontmap = {
|
@ -1,4 +1,4 @@
|
||||
require "event"
|
||||
require "ui/event"
|
||||
|
||||
-- constants from <linux/input.h>
|
||||
EV_KEY = 1
|
@ -1,5 +1,3 @@
|
||||
require "ui"
|
||||
|
||||
ReaderView = WidgetContainer:new{
|
||||
document = nil,
|
||||
|
@ -1,9 +1,9 @@
|
||||
require "ui"
|
||||
require "readerview"
|
||||
require "readerzooming"
|
||||
require "readerpanning"
|
||||
require "readerrotation"
|
||||
require "readerpaging"
|
||||
require "ui/ui"
|
||||
require "ui/reader/readerview"
|
||||
require "ui/reader/readerzooming"
|
||||
require "ui/reader/readerpanning"
|
||||
require "ui/reader/readerrotation"
|
||||
require "ui/reader/readerpaging"
|
||||
|
||||
--[[
|
||||
This is an abstraction for a reader interface
|
@ -1,8 +1,8 @@
|
||||
require "geometry"
|
||||
require "inputevent"
|
||||
require "widget"
|
||||
require "screen"
|
||||
require "dialog"
|
||||
require "ui/geometry"
|
||||
require "ui/inputevent"
|
||||
require "ui/widget"
|
||||
require "ui/screen"
|
||||
require "ui/dialog"
|
||||
require "settings" -- for debug(), TODO: put debug() somewhere else
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
require "rendertext"
|
||||
require "graphics"
|
||||
require "image"
|
||||
require "event"
|
||||
require "inputevent"
|
||||
require "font"
|
||||
require "ui/rendertext"
|
||||
require "ui/graphics"
|
||||
require "ui/image"
|
||||
require "ui/event"
|
||||
require "ui/inputevent"
|
||||
require "ui/font"
|
||||
|
||||
--[[
|
||||
This is a generic Widget interface
|
@ -1,128 +0,0 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "inputbox"
|
||||
require "selectmenu"
|
||||
require "commands"
|
||||
|
||||
HelpPage = {
|
||||
-- Other Class vars:
|
||||
|
||||
-- spacing between lines
|
||||
spacing = 25,
|
||||
|
||||
-- state buffer
|
||||
commands = nil,
|
||||
items = 0,
|
||||
page = 1,
|
||||
|
||||
-- font for displaying keys
|
||||
fsize = 20,
|
||||
face = Font:getFace("hpkfont", 20),
|
||||
|
||||
-- font for displaying help messages
|
||||
hfsize = 20,
|
||||
hface = Font:getFace("hfont", 20),
|
||||
|
||||
-- font for paging display
|
||||
ffsize = 15,
|
||||
fface = Font:getFace("pgfont", 15)
|
||||
}
|
||||
|
||||
-- Other Class vars:
|
||||
|
||||
|
||||
function HelpPage:show(ypos, height, commands)
|
||||
self.commands = {}
|
||||
self.items = 0
|
||||
local keys = {}
|
||||
for k,v in pairs(commands.map) do
|
||||
local key = v.keygroup or v.keydef:display()
|
||||
--debug("order: "..v.order.." command: "..tostring(v.keydef).." - keygroup:"..(v.keygroup or "nil").." -keys[key]:"..(keys[key] or "nil"))
|
||||
if keys[key] == nil then
|
||||
keys[key] = 1
|
||||
table.insert(self.commands,{shortcut=key,help=v.help,order=v.order})
|
||||
self.items = self.items + 1
|
||||
end
|
||||
end
|
||||
table.sort(self.commands,function(w1,w2) return w1.order<w2.order end)
|
||||
|
||||
local face_height, face_ascender = self.face.ftface:getHeightAndAscender()
|
||||
--local hface_height, hface_ascender = self.hface.ftface:getHeightAndAscender()
|
||||
local fface_height, fface_ascender = self.fface.ftface:getHeightAndAscender()
|
||||
--debug(face_height.."-"..face_ascender)
|
||||
--debug(fface_height.."-"..fface_ascender)
|
||||
face_height = math.ceil(face_height)
|
||||
face_ascender = math.ceil(face_ascender)
|
||||
fface_height = math.ceil(fface_height)
|
||||
fface_ascender = math.ceil(fface_ascender)
|
||||
local spacing = face_height + 5
|
||||
|
||||
local perpage = math.floor( (height - ypos - 1 * (fface_height + 5)) / spacing )
|
||||
local is_pagedirty = true
|
||||
|
||||
while true do
|
||||
if is_pagedirty then
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
local c
|
||||
local max_x = 0
|
||||
for c = 1, perpage do
|
||||
local x = 5
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
local key = self.commands[i].shortcut
|
||||
for _k,aMod in pairs(MOD_TABLE) do
|
||||
local modStart, modEnd = key:find(aMod.v)
|
||||
debug("key:"..key.." v:"..aMod.v.." d:"..aMod.d.." modstart:"..(modStart or "nil"))
|
||||
if(modStart ~= nil) then
|
||||
key = key:sub(1,modStart-1)..key:sub(modEnd+1)
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.face, aMod.d, true)
|
||||
fb.bb:paintRect(x, ypos + spacing*c - box.y_top, box.x, box.y_top + box.y_bottom, 4)
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.face, aMod.d.." + ", true)
|
||||
x = x + pen_x
|
||||
max_x = math.max(max_x, pen_x)
|
||||
end
|
||||
end
|
||||
debug("key:"..key)
|
||||
local box = sizeUtf8Text( x, fb.bb:getWidth(), self.face, key , true)
|
||||
fb.bb:paintRect(x, ypos + spacing*c - box.y_top, box.x, box.y_top + box.y_bottom, 4)
|
||||
local pen_x = renderUtf8Text(fb.bb, x, ypos + spacing*c, self.face, key, true)
|
||||
x = x + pen_x
|
||||
max_x = math.max(max_x, x)
|
||||
end
|
||||
end
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
renderUtf8Text(fb.bb, max_x + 20, ypos + spacing*c, self.hface, self.commands[i].help, true)
|
||||
end
|
||||
end
|
||||
renderUtf8Text(fb.bb, 5, height - fface_height + fface_ascender - 5, self.fface,
|
||||
"Page "..self.page.." of "..math.ceil(self.items / perpage).." - Back to close this page", true)
|
||||
end
|
||||
if is_pagedirty then
|
||||
fb:refresh(0, 0, ypos, fb.bb:getWidth(), height)
|
||||
is_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_PRESS then
|
||||
if ev.code == KEY_PGFWD or ev.code == KEY_LPGFWD then
|
||||
if self.page < (self.items / perpage) then
|
||||
self.page = self.page + 1
|
||||
is_pagedirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
is_pagedirty = true
|
||||
end
|
||||
elseif ev.code == KEY_BACK or ev.code == KEY_HOME then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,402 +0,0 @@
|
||||
require "font"
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- General inputbox
|
||||
----------------------------------------------------
|
||||
|
||||
InputBox = {
|
||||
-- Class vars:
|
||||
h = 100,
|
||||
input_slot_w = nil,
|
||||
input_start_x = 145,
|
||||
input_start_y = nil,
|
||||
input_cur_x = nil, -- points to the start of next input pos
|
||||
|
||||
input_bg = 0,
|
||||
|
||||
input_string = "",
|
||||
|
||||
shiftmode = false,
|
||||
altmode = false,
|
||||
|
||||
cursor = nil,
|
||||
|
||||
-- font for displaying input content
|
||||
-- we have to use mono here for better distance controlling
|
||||
face = Font:getFace("infont", 25),
|
||||
fheight = 25,
|
||||
fwidth = 15,
|
||||
commands = nil,
|
||||
initialized = false,
|
||||
}
|
||||
|
||||
function InputBox:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function InputBox:init()
|
||||
if not self.initialized then
|
||||
self:addAllCommands()
|
||||
self.initialized = true
|
||||
end
|
||||
end
|
||||
|
||||
function InputBox:refreshText()
|
||||
-- clear previous painted text
|
||||
fb.bb:paintRect(140, self.input_start_y-19,
|
||||
self.input_slot_w, self.fheight, self.input_bg)
|
||||
-- paint new text
|
||||
renderUtf8Text(fb.bb, self.input_start_x, self.input_start_y,
|
||||
self.face,
|
||||
self.input_string, 0)
|
||||
end
|
||||
|
||||
function InputBox:addChar(char)
|
||||
self.cursor:clear()
|
||||
|
||||
-- draw new text
|
||||
local cur_index = (self.cursor.x_pos + 3 - self.input_start_x)
|
||||
/ self.fwidth
|
||||
self.input_string = self.input_string:sub(1, cur_index)..char..
|
||||
self.input_string:sub(cur_index+1)
|
||||
self:refreshText()
|
||||
self.input_cur_x = self.input_cur_x + self.fwidth
|
||||
-- draw new cursor
|
||||
self.cursor:moveHorizontal(self.fwidth)
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:delChar()
|
||||
if self.input_start_x == self.input_cur_x then
|
||||
return
|
||||
end
|
||||
|
||||
local cur_index = (self.cursor.x_pos + 3 - self.input_start_x)
|
||||
/ self.fwidth
|
||||
if cur_index == 0 then return end
|
||||
|
||||
self.cursor:clear()
|
||||
|
||||
-- draw new text
|
||||
self.input_string = self.input_string:sub(1, cur_index-1)..
|
||||
self.input_string:sub(cur_index+1, -1)
|
||||
self:refreshText()
|
||||
self.input_cur_x = self.input_cur_x - self.fwidth
|
||||
|
||||
--fill last character with blank rectangle
|
||||
fb.bb:paintRect(self.input_cur_x, self.input_start_y-19,
|
||||
self.fwidth, self.fheight, self.input_bg)
|
||||
fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight)
|
||||
self.input_string = self.input_string:sub(0,-2)
|
||||
|
||||
-- draw new cursor
|
||||
self.cursor:moveHorizontal(-self.fwidth)
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:clearText()
|
||||
self.cursor:clear()
|
||||
self.input_string = ""
|
||||
self:refreshText()
|
||||
self.cursor.x_pos = self.input_start_x - 3
|
||||
self.cursor:draw()
|
||||
|
||||
fb:refresh(1, self.input_start_x-5, self.input_start_y-25,
|
||||
self.input_slot_w, self.h-25)
|
||||
end
|
||||
|
||||
function InputBox:drawHelpMsg(ypos, w, h)
|
||||
return
|
||||
end
|
||||
|
||||
function InputBox:drawBox(ypos, w, h, title)
|
||||
-- draw input border
|
||||
fb.bb:paintRect(20, ypos, w, h, 5)
|
||||
-- draw input slot
|
||||
fb.bb:paintRect(140, ypos + 10, w - 130, h - 20, self.input_bg)
|
||||
-- draw input title
|
||||
renderUtf8Text(fb.bb, 35, self.input_start_y, self.face,
|
||||
title, true)
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- InputBox:input()
|
||||
--
|
||||
-- @title: input prompt for the box
|
||||
-- @d_text: default to nil (used to set default text in input slot)
|
||||
-- @is_hint: if this arg is true, default text will be used as hint
|
||||
-- message for input
|
||||
----------------------------------------------------------------------
|
||||
function InputBox:input(ypos, height, title, d_text, is_hint)
|
||||
self:init()
|
||||
-- do some initilization
|
||||
self.ypos = ypos
|
||||
self.h = height
|
||||
self.input_start_y = ypos + 35
|
||||
self.input_cur_x = self.input_start_x
|
||||
self.input_slot_w = fb.bb:getWidth() - 170
|
||||
|
||||
self.cursor = Cursor:new {
|
||||
x_pos = self.input_start_x - 3,
|
||||
y_pos = ypos + 13,
|
||||
h = 30,
|
||||
}
|
||||
|
||||
|
||||
-- draw box and content
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawHelpMsg(ypos, w, h)
|
||||
self:drawBox(ypos, w, h, title)
|
||||
if d_text then
|
||||
if is_hint then
|
||||
-- print hint text
|
||||
fb.bb:paintRect(140, self.input_start_y-19,
|
||||
self.input_slot_w, self.fheight, self.input_bg)
|
||||
renderUtf8Text(fb.bb, self.input_start_x+5, self.input_start_y,
|
||||
self.face,
|
||||
d_text, 0)
|
||||
fb.bb:dimRect(140, self.input_start_y-19,
|
||||
self.input_slot_w, self.fheight, self.input_bg)
|
||||
else
|
||||
self.input_string = d_text
|
||||
self.input_cur_x = self.input_cur_x + (self.fwidth * d_text:len())
|
||||
self.cursor.x_pos = self.cursor.x_pos + (self.fwidth * d_text:len())
|
||||
self:refreshText()
|
||||
end
|
||||
end
|
||||
self.cursor:draw()
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
|
||||
while true do
|
||||
local ev = input.saveWaitForEvent()
|
||||
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
|
||||
ret_code = nil
|
||||
break
|
||||
end
|
||||
end -- if
|
||||
end -- while
|
||||
|
||||
local return_str = self.input_string
|
||||
self.input_string = ""
|
||||
return return_str
|
||||
end
|
||||
|
||||
function InputBox:addAllCommands()
|
||||
if self.commands then
|
||||
-- we only initialize once
|
||||
return
|
||||
end
|
||||
self.commands = Commands:new{}
|
||||
|
||||
INPUT_KEYS = {
|
||||
{KEY_Q, "q"}, {KEY_W, "w"}, {KEY_E, "e"}, {KEY_R, "r"}, {KEY_T, "t"},
|
||||
{KEY_Y, "y"}, {KEY_U, "u"}, {KEY_I, "i"}, {KEY_O, "o"}, {KEY_P, "p"},
|
||||
|
||||
{KEY_A, "a"}, {KEY_S, "s"}, {KEY_D, "d"}, {KEY_F, "f"}, {KEY_G, "g"},
|
||||
{KEY_H, "h"}, {KEY_J, "j"}, {KEY_K, "k"}, {KEY_L, "l"},
|
||||
|
||||
{KEY_Z, "z"}, {KEY_X, "x"}, {KEY_C, "c"}, {KEY_V, "v"}, {KEY_B, "b"},
|
||||
{KEY_N, "n"}, {KEY_M, "m"},
|
||||
|
||||
{KEY_1, "1"}, {KEY_2, "2"}, {KEY_3, "3"}, {KEY_4, "4"}, {KEY_5, "5"},
|
||||
{KEY_6, "6"}, {KEY_7, "7"}, {KEY_8, "8"}, {KEY_9, "9"}, {KEY_0, "0"},
|
||||
|
||||
{KEY_SPACE, " "},
|
||||
|
||||
-- DXG keys
|
||||
{KEY_DOT, "."}, {KEY_SLASH, "/"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_KEYS) do
|
||||
self.commands:add(v[1], nil, "",
|
||||
"input "..v[2],
|
||||
function(self)
|
||||
self:addChar(v[2])
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
self.commands:add(KEY_FW_LEFT, nil, "",
|
||||
"move cursor left",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) > self.input_start_x then
|
||||
self.cursor:moveHorizontalAndDraw(-self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "",
|
||||
"move cursor right",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) < self.input_cur_x then
|
||||
self.cursor:moveHorizontalAndDraw(self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"submit input content",
|
||||
function(self)
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "",
|
||||
"delete one character",
|
||||
function(self)
|
||||
self:delChar()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, MOD_SHIFT, "",
|
||||
"empty inputbox",
|
||||
function(self)
|
||||
self:clearText()
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_HOME}, nil, "",
|
||||
"cancel inputbox",
|
||||
function(self)
|
||||
self.input_string = nil
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- Inputbox for numbers only
|
||||
-- Designed by eLiNK
|
||||
----------------------------------------------------
|
||||
|
||||
NumInputBox = InputBox:new{
|
||||
initialized = false,
|
||||
commands = Commands:new{},
|
||||
}
|
||||
|
||||
function NumInputBox:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
INPUT_NUM_KEYS = {
|
||||
{KEY_Q, "1"}, {KEY_W, "2"}, {KEY_E, "3"}, {KEY_R, "4"}, {KEY_T, "5"},
|
||||
{KEY_Y, "6"}, {KEY_U, "7"}, {KEY_I, "8"}, {KEY_O, "9"}, {KEY_P, "0"},
|
||||
|
||||
{KEY_1, "1"}, {KEY_2, "2"}, {KEY_3, "3"}, {KEY_4, "4"}, {KEY_5, "5"},
|
||||
{KEY_6, "6"}, {KEY_7, "7"}, {KEY_8, "8"}, {KEY_9, "9"}, {KEY_0, "0"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_NUM_KEYS) do
|
||||
self.commands:add(v[1], nil, "",
|
||||
"input "..v[2],
|
||||
function(self)
|
||||
self:addChar(v[2])
|
||||
end
|
||||
)
|
||||
end -- for
|
||||
|
||||
self.commands:add(KEY_FW_LEFT, nil, "",
|
||||
"move cursor left",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) > self.input_start_x then
|
||||
self.cursor:moveHorizontalAndDraw(-self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "",
|
||||
"move cursor right",
|
||||
function(self)
|
||||
if (self.cursor.x_pos + 3) < self.input_cur_x then
|
||||
self.cursor:moveHorizontalAndDraw(self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, self.ypos,
|
||||
self.input_slot_w, self.h)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "",
|
||||
"submit input content",
|
||||
function(self)
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "",
|
||||
"delete one character",
|
||||
function(self)
|
||||
self:delChar()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, MOD_SHIFT, "",
|
||||
"empty inputbox",
|
||||
function(self)
|
||||
self:clearText()
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_HOME}, nil, "",
|
||||
"cancel inputbox",
|
||||
function(self)
|
||||
self.input_string = nil
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function NumInputBox:drawHelpMsg(ypos, w, h)
|
||||
local w = 415
|
||||
local y = ypos - 60
|
||||
local x = (G_width - w) / 2
|
||||
local h = 50
|
||||
local bw = 2
|
||||
local face = Font:getFace("scfont", 22)
|
||||
|
||||
fb.bb:paintRect(x, y, w, h, 15)
|
||||
fb.bb:paintRect(x+bw, y+bw, w-2*bw, h-2*bw, 0)
|
||||
|
||||
local font_y = y + 22
|
||||
local font_x = x + 22
|
||||
INPUT_NUM_KEYS = {
|
||||
{"Q", "1"}, {"W", "2"}, {"E", "3"}, {"R", "4"}, {"T", "5"},
|
||||
{"Y", "6"}, {"U", "7"}, {"I", "8"}, {"O", "9"}, {"P", "0"},
|
||||
}
|
||||
for k,v in ipairs(INPUT_NUM_KEYS) do
|
||||
renderUtf8Text(fb.bb, font_x, font_y, face,
|
||||
v[1], true)
|
||||
renderUtf8Text(fb.bb, font_x, font_y + 22, face,
|
||||
v[2], true)
|
||||
font_x = font_x + 40
|
||||
end
|
||||
|
||||
fb:refresh(1, x, y, w, h)
|
||||
end
|
@ -1,278 +0,0 @@
|
||||
--[[
|
||||
This file contains settings related to key codes
|
||||
|
||||
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
This file is based on include/keydefs.h from "launchpad"
|
||||
application, which is
|
||||
Copyright (C) 2010 Andy M. aka h1uke h1ukeguy @ gmail.com
|
||||
and was licensed under the GPLv2
|
||||
]]--
|
||||
|
||||
Keys = {
|
||||
altmode = false,
|
||||
shiftmode = false,
|
||||
}
|
||||
|
||||
KEY_1 = 2
|
||||
KEY_2 = 3
|
||||
KEY_3 = 4
|
||||
KEY_4 = 5
|
||||
KEY_5 = 6
|
||||
KEY_6 = 7
|
||||
KEY_7 = 8
|
||||
KEY_8 = 9
|
||||
KEY_9 = 10
|
||||
KEY_0 = 11
|
||||
KEY_Q = 16
|
||||
KEY_W = 17
|
||||
KEY_E = 18
|
||||
KEY_R = 19
|
||||
KEY_T = 20
|
||||
KEY_Y = 21
|
||||
KEY_U = 22
|
||||
KEY_I = 23
|
||||
KEY_O = 24
|
||||
KEY_P = 25
|
||||
KEY_A = 30
|
||||
KEY_S = 31
|
||||
KEY_D = 32
|
||||
KEY_F = 33
|
||||
KEY_G = 34
|
||||
KEY_H = 35
|
||||
KEY_J = 36
|
||||
KEY_K = 37
|
||||
KEY_L = 38
|
||||
KEY_DEL = 14
|
||||
KEY_Z = 44
|
||||
KEY_X = 45
|
||||
KEY_C = 46
|
||||
KEY_V = 47
|
||||
KEY_B = 48
|
||||
KEY_N = 49
|
||||
KEY_M = 50
|
||||
KEY_DOT = 52
|
||||
KEY_SLASH = 53
|
||||
KEY_ENTER = 28
|
||||
KEY_SHIFT = 42
|
||||
KEY_ALT = 56
|
||||
KEY_SPACE = 57
|
||||
KEY_AA = 90
|
||||
KEY_SYM = 94
|
||||
KEY_VPLUS = 115
|
||||
KEY_VMINUS = 114
|
||||
KEY_HOME = 98
|
||||
KEY_PGBCK = 109
|
||||
KEY_PGFWD = 124
|
||||
KEY_MENU = 139
|
||||
KEY_BACK = 91
|
||||
KEY_FW_LEFT = 105
|
||||
KEY_FW_RIGHT = 106
|
||||
KEY_FW_UP = 122
|
||||
KEY_FW_DOWN = 123
|
||||
KEY_FW_PRESS = 92
|
||||
|
||||
KEY_INTO_SCREEN_SAVER = 10000
|
||||
KEY_OUTOF_SCREEN_SAVER = 10001
|
||||
|
||||
-- constants from <linux/input.h>
|
||||
EV_KEY = 1
|
||||
|
||||
-- event values
|
||||
EVENT_VALUE_KEY_PRESS = 1
|
||||
EVENT_VALUE_KEY_REPEAT = 2
|
||||
EVENT_VALUE_KEY_RELEASE = 0
|
||||
|
||||
-- modifiers
|
||||
MOD_SHIFT = "_Shift_"
|
||||
MOD_ALT = "_Alt_"
|
||||
MOD_SHIFT_OR_ALT = "_ShiftAlt_"
|
||||
MOD_ANY = "_Any_"
|
||||
MOD_SHIFT_DISPLAY = "Shift"
|
||||
MOD_ALT_DISPLAY = "Alt"
|
||||
MOD_TABLE = {MOD_SHIFT={v=MOD_SHIFT,d=MOD_SHIFT_DISPLAY},MOD_ALT={v=MOD_ALT,d=MOD_ALT_DISPLAY}}
|
||||
|
||||
function getKeyModifier()
|
||||
return Keys.altmode and MOD_ALT or Keys.shiftmode and MOD_SHIFT
|
||||
end
|
||||
|
||||
function setK3Keycodes()
|
||||
KEY_AA = 190
|
||||
KEY_SYM = 126
|
||||
KEY_HOME = 102
|
||||
KEY_BACK = 158
|
||||
KEY_PGFWD = 191
|
||||
KEY_LPGBCK = 193
|
||||
KEY_LPGFWD = 104
|
||||
KEY_VPLUS = 115
|
||||
KEY_VMINUS = 114
|
||||
KEY_FW_UP = 103
|
||||
KEY_FW_DOWN = 108
|
||||
KEY_FW_PRESS = 194
|
||||
end
|
||||
|
||||
function setEmuKeycodes()
|
||||
KEY_PGFWD = 117
|
||||
KEY_PGBCK = 112
|
||||
KEY_LPGBCK = 72 -- F6
|
||||
KEY_LPGFWD = 73 -- F7
|
||||
KEY_HOME = 110 -- home
|
||||
KEY_BACK = 22 -- backspace
|
||||
KEY_DEL = 119 -- Delete
|
||||
KEY_MENU = 67 -- F1
|
||||
KEY_FW_UP = 111
|
||||
KEY_FW_DOWN = 116
|
||||
KEY_FW_LEFT = 113
|
||||
KEY_FW_RIGHT = 114
|
||||
KEY_FW_PRESS = 115 -- end for now (above arrows)
|
||||
KEY_SPACE = 65
|
||||
|
||||
KEY_ENTER = 36
|
||||
|
||||
KEY_1 = 10
|
||||
KEY_2 = 11
|
||||
KEY_3 = 12
|
||||
KEY_4 = 13
|
||||
KEY_5 = 14
|
||||
KEY_6 = 15
|
||||
KEY_7 = 16
|
||||
KEY_8 = 17
|
||||
KEY_9 = 18
|
||||
KEY_0 = 19
|
||||
|
||||
KEY_Q = 24
|
||||
KEY_W = 25
|
||||
KEY_E = 26
|
||||
KEY_R = 27
|
||||
KEY_T = 28
|
||||
KEY_Y = 29
|
||||
KEY_U = 30
|
||||
KEY_I = 31
|
||||
KEY_O = 32
|
||||
KEY_P = 33
|
||||
|
||||
KEY_A = 38
|
||||
KEY_S = 39
|
||||
KEY_D = 40
|
||||
KEY_F = 41
|
||||
KEY_G = 42
|
||||
KEY_H = 43
|
||||
KEY_J = 44
|
||||
KEY_K = 45
|
||||
KEY_L = 46
|
||||
|
||||
KEY_Z = 52
|
||||
KEY_X = 53
|
||||
KEY_C = 54
|
||||
KEY_V = 55
|
||||
KEY_B = 56
|
||||
KEY_N = 57
|
||||
KEY_M = 58
|
||||
|
||||
KEY_DOT = 60
|
||||
KEY_SLASH = 61
|
||||
|
||||
KEY_AA = 105 -- right alt
|
||||
KEY_SYM = 62 -- right shift
|
||||
|
||||
KEY_SHIFT = 50 -- left shift
|
||||
KEY_ALT = 64 -- left alt
|
||||
KEY_VPLUS = 95 -- F11
|
||||
KEY_VMINUS = 96 -- F12
|
||||
end
|
||||
|
||||
|
||||
function adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
if ev.code == KEY_SHIFT then
|
||||
Keys.shiftmode = true
|
||||
elseif ev.code == KEY_ALT then
|
||||
Keys.altmode = true
|
||||
end
|
||||
elseif ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_RELEASE then
|
||||
if ev.code == KEY_SHIFT then
|
||||
Keys.shiftmode = false
|
||||
elseif ev.code == KEY_ALT then
|
||||
Keys.altmode = false
|
||||
end
|
||||
end
|
||||
|
||||
-- adjust five way key according to rotation mode
|
||||
local code = ev.code
|
||||
if Screen.cur_rotation_mode == 0 then
|
||||
return code
|
||||
elseif Screen.cur_rotation_mode == 1 then
|
||||
if code == KEY_FW_UP then
|
||||
return KEY_FW_RIGHT
|
||||
elseif code == KEY_FW_RIGHT then
|
||||
return KEY_FW_DOWN
|
||||
elseif code == KEY_FW_DOWN then
|
||||
return KEY_FW_LEFT
|
||||
elseif code == KEY_FW_LEFT then
|
||||
return KEY_FW_UP
|
||||
else
|
||||
return code
|
||||
end
|
||||
elseif Screen.cur_rotation_mode == 2 then
|
||||
if code == KEY_FW_UP then
|
||||
return KEY_FW_DOWN
|
||||
elseif code == KEY_FW_RIGHT then
|
||||
return KEY_FW_LEFT
|
||||
elseif code == KEY_FW_DOWN then
|
||||
return KEY_FW_UP
|
||||
elseif code == KEY_FW_LEFT then
|
||||
return KEY_FW_RIGHT
|
||||
else
|
||||
return code
|
||||
end
|
||||
elseif Screen.cur_rotation_mode == 3 then
|
||||
if code == KEY_FW_UP then
|
||||
return KEY_FW_LEFT
|
||||
elseif code == KEY_FW_RIGHT then
|
||||
return KEY_FW_UP
|
||||
elseif code == KEY_FW_DOWN then
|
||||
return KEY_FW_RIGHT
|
||||
elseif code == KEY_FW_LEFT then
|
||||
return KEY_FW_DOWN
|
||||
else
|
||||
return code
|
||||
end
|
||||
end
|
||||
-- This should not happen.
|
||||
debug("# Unrecognizable rotation mode "..Screen.cur_rotation_mode.."!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- wrapper for input.waitForEvents that will retry for some cases
|
||||
function input.saveWaitForEvent(timeout)
|
||||
local retry = true
|
||||
while retry do
|
||||
local ok, ev = pcall(input.waitForEvent, timeout)
|
||||
if not ok then
|
||||
debug("got error waiting for events:", ev)
|
||||
if ev == "Waiting for input failed: 4\n" then
|
||||
-- EINTR, we got interrupted. Try and restart
|
||||
retry = true
|
||||
else
|
||||
retry = false
|
||||
end
|
||||
else
|
||||
return ev
|
||||
end
|
||||
end
|
||||
end
|
@ -1,47 +0,0 @@
|
||||
require "unireader"
|
||||
require "inputbox"
|
||||
|
||||
PDFReader = UniReader:new{}
|
||||
|
||||
-- open a PDF file and its settings store
|
||||
function PDFReader:open(filename)
|
||||
-- muPDF manages its own cache, set second parameter
|
||||
-- to the maximum size you want it to grow
|
||||
local ok
|
||||
ok, self.doc = pcall(pdf.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return false, self.doc -- will contain error message
|
||||
end
|
||||
if self.doc:needsPassword() then
|
||||
local password = InputBox:input(G_height-100, 100, "Pass:")
|
||||
if not password or not self.doc:authenticatePassword(password) then
|
||||
self.doc:close()
|
||||
self.doc = nil
|
||||
return false, "wrong or missing password"
|
||||
end
|
||||
-- password wrong or not entered
|
||||
end
|
||||
local ok, err = pcall(self.doc.getPages, self.doc)
|
||||
if not ok then
|
||||
-- for PDFs, they might trigger errors later when accessing page tree
|
||||
self.doc:close()
|
||||
self.doc = nil
|
||||
return false, "damaged page tree"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- highlight support
|
||||
----------------------------------------------------
|
||||
function PDFReader:getText(pageno)
|
||||
local ok, page = pcall(self.doc.openPage, self.doc, pageno)
|
||||
if not ok then
|
||||
-- TODO: error handling
|
||||
return nil
|
||||
end
|
||||
local text = page:getPageText()
|
||||
--debug("## page:getPageText "..dump(text)) -- performance impact on device
|
||||
page:close()
|
||||
return text
|
||||
end
|
@ -1,179 +0,0 @@
|
||||
#!./kpdfview
|
||||
--[[
|
||||
KindlePDFViewer: a reader implementation
|
||||
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]--
|
||||
|
||||
require "alt_getopt"
|
||||
require "pdfreader"
|
||||
require "djvureader"
|
||||
require "crereader"
|
||||
require "filechooser"
|
||||
require "settings"
|
||||
require "screen"
|
||||
require "keys"
|
||||
require "commands"
|
||||
require "dialog"
|
||||
|
||||
-- option parsing:
|
||||
longopts = {
|
||||
password = "p",
|
||||
goto = "g",
|
||||
gamma = "G",
|
||||
debug = "d",
|
||||
help = "h"
|
||||
}
|
||||
|
||||
function openFile(filename)
|
||||
local file_type = string.lower(string.match(filename, ".+%.([^.]+)"))
|
||||
local reader = nil
|
||||
if file_type == "djvu" then
|
||||
reader = DJVUReader
|
||||
elseif file_type == "pdf" or file_type == "xps" or file_type == "cbz" then
|
||||
reader = PDFReader
|
||||
elseif file_type == "epub" or file_type == "txt" or file_type == "rtf" or file_type == "htm" or file_type == "html" or file_type == "fb2" or file_type == "chm" or file_type == "mobi" or file_type == "doc" or file_type == "zip" then
|
||||
reader = CREReader
|
||||
end
|
||||
if reader then
|
||||
InfoMessage:show("Opening document, please wait... ", 0)
|
||||
reader:preLoadSettings(filename)
|
||||
local ok, err = reader:open(filename)
|
||||
if ok then
|
||||
reader:loadSettings(filename)
|
||||
page_num = reader:getLastPageOrPos()
|
||||
reader:goto(tonumber(page_num), true)
|
||||
reader_settings:saveSetting("lastfile", filename)
|
||||
return reader:inputLoop()
|
||||
else
|
||||
InfoMessage:show("Error opening document.", 0)
|
||||
util.sleep(2)
|
||||
end
|
||||
end
|
||||
return true -- on failed attempts, we signal to keep running
|
||||
end
|
||||
|
||||
function showusage()
|
||||
print("usage: ./reader.lua [OPTION] ... path")
|
||||
print("Read PDFs and DJVUs on your E-Ink reader")
|
||||
print("")
|
||||
print("-p, --password=PASSWORD set password for reading PDF document")
|
||||
print("-g, --goto=page start reading on page")
|
||||
print("-G, --gamma=GAMMA set gamma correction")
|
||||
print("-d, --debug start in debug mode")
|
||||
print(" (floating point notation, e.g. \"1.5\")")
|
||||
print("-h, --help show this usage help")
|
||||
print("")
|
||||
print("If you give the name of a directory instead of a file path, a file")
|
||||
print("chooser will show up and let you select a PDF|DJVU file")
|
||||
print("")
|
||||
print("If you don't pass any path, the last viewed document will be opened")
|
||||
print("")
|
||||
print("This software is licensed under the GPLv3.")
|
||||
print("See http://github.com/hwhw/kindlepdfviewer for more info.")
|
||||
return
|
||||
end
|
||||
|
||||
optarg, optind = alt_getopt.get_opts(ARGV, "p:g:G:hg:dg:", longopts)
|
||||
if optarg["h"] then
|
||||
return showusage()
|
||||
end
|
||||
|
||||
if not optarg["d"] then
|
||||
debug = function() end
|
||||
end
|
||||
|
||||
if optarg["G"] ~= nil then
|
||||
globalgamma = optarg["G"]
|
||||
end
|
||||
|
||||
if util.isEmulated()==1 then
|
||||
input.open("")
|
||||
-- SDL key codes
|
||||
setEmuKeycodes()
|
||||
else
|
||||
input.open("slider")
|
||||
input.open("/dev/input/event0")
|
||||
input.open("/dev/input/event1")
|
||||
|
||||
-- check if we are running on Kindle 3 (additional volume input)
|
||||
local f=lfs.attributes("/dev/input/event2")
|
||||
if f then
|
||||
print("Auto-detected Kindle 3")
|
||||
input.open("/dev/input/event2")
|
||||
setK3Keycodes()
|
||||
end
|
||||
end
|
||||
|
||||
fb = einkfb.open("/dev/fb0")
|
||||
G_width, G_height = fb:getSize()
|
||||
-- read current rotation mode
|
||||
Screen:updateRotationMode()
|
||||
Screen.native_rotation_mode = Screen.cur_rotation_mode
|
||||
|
||||
-- set up reader's setting: font
|
||||
reader_settings = DocSettings:open(".reader")
|
||||
fontmap = reader_settings:readSetting("fontmap")
|
||||
if fontmap ~= nil then
|
||||
Font.fontmap = fontmap
|
||||
end
|
||||
|
||||
-- initialize global settings shared among all readers
|
||||
UniReader:initGlobalSettings(reader_settings)
|
||||
-- initialize specific readers
|
||||
PDFReader:init()
|
||||
DJVUReader:init()
|
||||
CREReader:init()
|
||||
|
||||
-- display directory or open file
|
||||
local patharg = reader_settings:readSetting("lastfile")
|
||||
if ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "directory" then
|
||||
local running = true
|
||||
FileChooser:setPath(ARGV[optind])
|
||||
while running do
|
||||
local file, callback = FileChooser:choose(0, G_height)
|
||||
if callback then
|
||||
callback()
|
||||
else
|
||||
if file ~= nil then
|
||||
running = openFile(file)
|
||||
print(file)
|
||||
else
|
||||
running = false
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "file" then
|
||||
openFile(ARGV[optind], optarg["p"])
|
||||
elseif patharg and lfs.attributes(patharg, "mode") == "file" then
|
||||
openFile(patharg, optarg["p"])
|
||||
else
|
||||
return showusage()
|
||||
end
|
||||
|
||||
|
||||
-- save reader settings
|
||||
reader_settings:saveSetting("fontmap", Font.fontmap)
|
||||
reader_settings:close()
|
||||
|
||||
-- @TODO dirty workaround, find a way to force native system poll
|
||||
-- screen orientation and upside down mode 09.03 2012
|
||||
fb:setOrientation(Screen.native_rotation_mode)
|
||||
|
||||
input.closeAll()
|
||||
if util.isEmulated()==0 then
|
||||
os.execute("killall -cont cvm")
|
||||
os.execute('echo "send '..KEY_MENU..'" > /proc/keypad;echo "send '..KEY_MENU..'" > /proc/keypad')
|
||||
end
|
@ -1,343 +0,0 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "commands"
|
||||
|
||||
SelectMenu = {
|
||||
-- font for displaying item names
|
||||
fsize = 22,
|
||||
-- font for page title
|
||||
tfsize = 25,
|
||||
-- font for paging display
|
||||
ffsize = 16,
|
||||
-- font for item shortcut
|
||||
sface = Font:getFace("scfont", 22),
|
||||
|
||||
-- title height
|
||||
title_H = 40,
|
||||
-- spacing between lines
|
||||
spacing = 36,
|
||||
-- foot height
|
||||
foot_H = 27,
|
||||
|
||||
menu_title = "No Title",
|
||||
no_item_msg = "No items found.",
|
||||
item_array = {},
|
||||
items = 0,
|
||||
|
||||
item_shortcuts = {
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
|
||||
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Del",
|
||||
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Ent",
|
||||
},
|
||||
last_shortcut = 0,
|
||||
|
||||
-- state buffer
|
||||
page = 1,
|
||||
current = 1,
|
||||
oldcurrent = 0,
|
||||
selected_item = nil,
|
||||
|
||||
commands = nil,
|
||||
}
|
||||
|
||||
function SelectMenu:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.items = #o.item_array
|
||||
o.page = 1
|
||||
o.current = 1
|
||||
o.oldcurrent = 0
|
||||
o.selected_item = nil
|
||||
-- increase spacing for DXG so we don't have more than 30 shortcuts
|
||||
if fb.bb:getHeight() == 1200 then
|
||||
o.spacing = 37
|
||||
end
|
||||
o:addAllCommands()
|
||||
return o
|
||||
end
|
||||
|
||||
function SelectMenu:getItemIndexByShortCut(c, perpage)
|
||||
if c == nil then return end -- unused key
|
||||
for _k,_v in ipairs(self.item_shortcuts) do
|
||||
if _v == c and _k <= self.last_shortcut then
|
||||
return (perpage * (self.page - 1) + _k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SelectMenu:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_FW_UP, nil, "",
|
||||
"previous item",
|
||||
function(sm)
|
||||
if sm.current == 1 then
|
||||
if sm.page > 1 then
|
||||
sm.current = sm.perpage
|
||||
sm.page = sm.page - 1
|
||||
sm.pagedirty = true
|
||||
end
|
||||
else
|
||||
sm.current = sm.current - 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "",
|
||||
"next item",
|
||||
function(sm)
|
||||
if sm.current == sm.perpage then
|
||||
if sm.page < (sm.items / sm.perpage) then
|
||||
sm.current = 1
|
||||
sm.page = sm.page + 1
|
||||
sm.pagedirty = true
|
||||
end
|
||||
else
|
||||
if sm.page ~= math.floor(sm.items / sm.perpage) + 1
|
||||
or sm.current + (sm.page - 1) * sm.perpage < sm.items then
|
||||
sm.current = sm.current + 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, "",
|
||||
"next page",
|
||||
function(sm)
|
||||
if sm.page < (sm.items / sm.perpage) then
|
||||
if sm.current + sm.page * sm.perpage > sm.items then
|
||||
sm.current = sm.items - sm.page * sm.perpage
|
||||
end
|
||||
sm.page = sm.page + 1
|
||||
sm.pagedirty = true
|
||||
else
|
||||
sm.current = sm.items - (sm.page - 1) * sm.perpage
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "",
|
||||
"previous page",
|
||||
function(sm)
|
||||
if sm.page > 1 then
|
||||
sm.page = sm.page - 1
|
||||
sm.pagedirty = true
|
||||
else
|
||||
sm.current = 1
|
||||
sm.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_PRESS, nil, "",
|
||||
"select menu item",
|
||||
function(sm)
|
||||
if sm.items == 0 then
|
||||
return "break"
|
||||
else
|
||||
self.selected_item = (sm.perpage * (sm.page - 1) + sm.current)
|
||||
end
|
||||
end
|
||||
)
|
||||
local KEY_Q_to_P = {}
|
||||
for i = KEY_Q, KEY_P do
|
||||
table.insert(KEY_Q_to_P, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("Q to P", KEY_Q_to_P,
|
||||
"Select menu item with Q to E key as shortcut",
|
||||
function(sm, keydef)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(
|
||||
sm.item_shortcuts[ keydef.keycode - KEY_Q + 1 ], sm.perpage)
|
||||
end
|
||||
)
|
||||
local KEY_A_to_L = {}
|
||||
for i = KEY_A, KEY_L do
|
||||
table.insert(KEY_A_to_L, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("A to L", KEY_A_to_L,
|
||||
"Select menu item with A to L key as shortcut",
|
||||
function(sm, keydef)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(
|
||||
sm.item_shortcuts[ keydef.keycode - KEY_A + 11 ], sm.perpage)
|
||||
end
|
||||
)
|
||||
local KEY_Z_to_M = {}
|
||||
for i = KEY_Z, KEY_M do
|
||||
table.insert(KEY_Z_to_M, Keydef:new(i, nil, ""))
|
||||
end
|
||||
self.commands:addGroup("Z to M", KEY_Z_to_M,
|
||||
"Select menu item with Z to M key as shortcut",
|
||||
function(sm, keydef)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(
|
||||
sm.item_shortcuts[ keydef.keycode - KEY_Z + 21 ], sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "",
|
||||
"Select menu item with del key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Del", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DOT, nil, "",
|
||||
"Select menu item with dot key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut(".", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_SYM, KEY_SLASH}, nil, "",
|
||||
"Select menu item with sym/slash key as shortcut",
|
||||
function(sm)
|
||||
-- DXG has slash after dot
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Sym", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_ENTER, nil, "",
|
||||
"Select menu item with enter key as shortcut",
|
||||
function(sm)
|
||||
sm.selected_item = sm:getItemIndexByShortCut("Ent", sm.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, nil, "",
|
||||
"Exit menu",
|
||||
function(sm)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function SelectMenu:clearCommands()
|
||||
self.commands = Commands:new{}
|
||||
|
||||
self.commands:add(KEY_BACK, nil, "",
|
||||
"Exit menu",
|
||||
function(sm)
|
||||
return "break"
|
||||
end)
|
||||
end
|
||||
|
||||
------------------------------------------------
|
||||
-- return the index of selected item
|
||||
------------------------------------------------
|
||||
function SelectMenu:choose(ypos, height)
|
||||
self.perpage = math.floor(height / self.spacing) - 2
|
||||
self.pagedirty = true
|
||||
self.markerdirty = false
|
||||
self.last_shortcut = 0
|
||||
|
||||
while true do
|
||||
local cface = Font:getFace("cfont", 22)
|
||||
local tface = Font:getFace("tfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if self.pagedirty then
|
||||
self.markerdirty = true
|
||||
-- draw menu title
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), self.title_H + 10, 0)
|
||||
fb.bb:paintRect(10, ypos + 10, fb.bb:getWidth() - 20, self.title_H, 5)
|
||||
|
||||
local x = 20
|
||||
local y = ypos + self.title_H
|
||||
renderUtf8Text(fb.bb, x, y, tface, self.menu_title, true)
|
||||
|
||||
-- draw items
|
||||
fb.bb:paintRect(0, ypos + self.title_H + 10, fb.bb:getWidth(), height - self.title_H, 0)
|
||||
if self.items == 0 then
|
||||
y = ypos + self.title_H + (self.spacing * 2)
|
||||
renderUtf8Text(fb.bb, 30, y, cface,
|
||||
"Oops... Bad news for you:", true)
|
||||
y = y + self.spacing
|
||||
renderUtf8Text(fb.bb, 30, y, cface,
|
||||
self.no_item_msg, true)
|
||||
self.markerdirty = false
|
||||
self:clearCommands()
|
||||
else
|
||||
local c
|
||||
for c = 1, self.perpage do
|
||||
local i = (self.page - 1) * self.perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
|
||||
-- paint shortcut indications
|
||||
if c <= 10 or c > 20 then
|
||||
blitbuffer.paintBorder(fb.bb, 10, y-22, 29, 29, 2, 15)
|
||||
else
|
||||
fb.bb:paintRect(10, y-22, 29, 29, 3)
|
||||
end
|
||||
if self.item_shortcuts[c] ~= nil and
|
||||
string.len(self.item_shortcuts[c]) == 3 then
|
||||
-- debug "Del", "Sym and "Ent"
|
||||
renderUtf8Text(fb.bb, 13, y, fface,
|
||||
self.item_shortcuts[c], true)
|
||||
else
|
||||
renderUtf8Text(fb.bb, 18, y, self.sface,
|
||||
self.item_shortcuts[c], true)
|
||||
end
|
||||
|
||||
self.last_shortcut = c
|
||||
|
||||
renderUtf8Text(fb.bb, 50, y, cface,
|
||||
self.item_array[i], true)
|
||||
end -- if i <= self.items
|
||||
end -- for c=1, self.perpage
|
||||
end -- if self.items == 0
|
||||
|
||||
-- draw footer
|
||||
y = ypos + self.title_H + (self.spacing * self.perpage)
|
||||
+ self.foot_H + 5
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
renderUtf8Text(fb.bb, x, y, fface,
|
||||
"Page "..self.page.." of "..
|
||||
(math.ceil(self.items / self.perpage)), true)
|
||||
end
|
||||
|
||||
if self.markerdirty then
|
||||
if not self.pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 8
|
||||
fb.bb:paintRect(45, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 45, y, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = ypos + self.title_H + (self.spacing * self.current) + 8
|
||||
fb.bb:paintRect(45, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not self.pagedirty then
|
||||
fb:refresh(1, 45, y, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if self.pagedirty then
|
||||
fb:refresh(0, 0, ypos, fb.bb:getWidth(), height)
|
||||
self.pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.saveWaitForEvent()
|
||||
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, self.item_array[self.selected_item]
|
||||
end
|
||||
end -- EOF if
|
||||
end -- EOF while
|
||||
return nil
|
||||
end
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue