mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Merge pull request #15 from houqp/master
fixes for fontchooser & add fileseacher & new rotation implementation & toc menu & jump stack & bug fixes
This commit is contained in:
2
Makefile
2
Makefile
@@ -7,7 +7,7 @@ MUPDFLIBDIR=$(MUPDFDIR)/$(MUPDFTARGET)
|
||||
|
||||
SQLITE3DIR=sqlite-amalgamation-3070900
|
||||
LSQLITE3DIR=lsqlite3_svn08
|
||||
FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.4
|
||||
FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.8
|
||||
LFSDIR=luafilesystem
|
||||
|
||||
# set this to your ARM cross compiler:
|
||||
|
||||
112
filechooser.lua
112
filechooser.lua
@@ -2,16 +2,26 @@ require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "fontchooser"
|
||||
require "filesearcher"
|
||||
require "inputbox"
|
||||
require "selectmenu"
|
||||
|
||||
FileChooser = {
|
||||
-- Class vars:
|
||||
|
||||
-- font for displaying file/dir names
|
||||
face = freetype.newBuiltinFace("sans", 25),
|
||||
fhash = "s25",
|
||||
-- font for displaying toc item names
|
||||
fsize = 25,
|
||||
face = nil,
|
||||
fhash = nil,
|
||||
--face = freetype.newBuiltinFace("sans", 25),
|
||||
--fhash = "s25",
|
||||
|
||||
-- font for paging display
|
||||
sface = freetype.newBuiltinFace("sans", 16),
|
||||
sfhash = "s16",
|
||||
ffsize = 16,
|
||||
fface = nil,
|
||||
ffhash = nil,
|
||||
--sface = freetype.newBuiltinFace("sans", 16),
|
||||
--sfhash = "s16",
|
||||
|
||||
-- spacing between lines
|
||||
spacing = 40,
|
||||
|
||||
@@ -35,6 +45,7 @@ function FileChooser:readdir()
|
||||
table.insert(self.files, f)
|
||||
end
|
||||
end
|
||||
--@TODO make sure .. is sortted to the first item 16.02 2012
|
||||
table.sort(self.dirs)
|
||||
table.sort(self.files)
|
||||
end
|
||||
@@ -51,32 +62,15 @@ function FileChooser:setPath(newPath)
|
||||
return true
|
||||
end
|
||||
|
||||
function FileChooser:rotationMode()
|
||||
--[[
|
||||
return code for four kinds of rotation mode:
|
||||
|
||||
0 for no rotation,
|
||||
1 for landscape with bottom on the right side of screen, etc.
|
||||
|
||||
2
|
||||
---------
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
3 | | 1
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
---------
|
||||
0
|
||||
--]]
|
||||
if KEY_FW_DOWN == 116 then
|
||||
return 0
|
||||
function FileChooser:updateFont()
|
||||
if self.fhash ~= FontChooser.cfont..self.fsize then
|
||||
self.face = freetype.newBuiltinFace(FontChooser.cfont, self.fsize)
|
||||
self.fhash = FontChooser.cfont..self.fsize
|
||||
end
|
||||
if self.ffhash ~= FontChooser.ffont..self.ffsize then
|
||||
self.fface = freetype.newBuiltinFace(FontChooser.ffont, self.ffsize)
|
||||
self.ffhash = FontChooser.ffont..self.ffsize
|
||||
end
|
||||
orie_fd = assert(io.open("/sys/module/eink_fb_hal_broads/parameters/bs_orientation", "r"))
|
||||
updown_fd = assert(io.open("/sys/module/eink_fb_hal_broads/parameters/bs_upside_down", "r"))
|
||||
mode = orie_fd:read() + (updown_fd:read() * 2)
|
||||
return mode
|
||||
end
|
||||
|
||||
function FileChooser:choose(ypos, height)
|
||||
@@ -114,6 +108,7 @@ function FileChooser:choose(ypos, height)
|
||||
end
|
||||
|
||||
while true do
|
||||
self:updateFont()
|
||||
if pagedirty then
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
local c
|
||||
@@ -127,7 +122,7 @@ function FileChooser:choose(ypos, height)
|
||||
renderUtf8Text(fb.bb, 50, ypos + self.spacing*c, self.face, self.fhash, self.files[i-#self.dirs], true)
|
||||
end
|
||||
end
|
||||
renderUtf8Text(fb.bb, 39, ypos + self.spacing * perpage + 32, self.sface, self.sfhash,
|
||||
renderUtf8Text(fb.bb, 39, ypos + self.spacing * perpage + 32, self.fface, self.ffhash,
|
||||
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
|
||||
markerdirty = true
|
||||
end
|
||||
@@ -151,36 +146,35 @@ function FileChooser:choose(ypos, height)
|
||||
end
|
||||
local ev = input.waitForEvent()
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
--print("key code:"..ev.code)
|
||||
ev.code = adjustFWKey(ev.code)
|
||||
if ev.code == KEY_FW_UP then
|
||||
if self:rotationMode() == 0 then
|
||||
prevItem()
|
||||
elseif self:rotationMode() == 2 then
|
||||
nextItem()
|
||||
end
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
if self:rotationMode() == 0 then
|
||||
nextItem()
|
||||
elseif self:rotationMode() == 2 then
|
||||
prevItem()
|
||||
end
|
||||
elseif ev.code == KEY_FW_LEFT then
|
||||
if self:rotationMode() == 1 then
|
||||
prevItem()
|
||||
elseif self:rotationMode() == 3 then
|
||||
nextItem()
|
||||
end
|
||||
elseif ev.code == KEY_FW_RIGHT then
|
||||
if self:rotationMode() == 1 then
|
||||
nextItem()
|
||||
elseif self:rotationMode() == 3 then
|
||||
prevItem()
|
||||
end
|
||||
elseif ev.code == KEY_F then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_F then -- invoke fontchooser menu
|
||||
FontChooser:init()
|
||||
newfont = FontChooser:choose(0, height)
|
||||
if newfont ~= nil then
|
||||
self.face = freetype.newBuiltinFace(newfont, 25)
|
||||
clearglyphcache()
|
||||
fonts_menu = SelectMenu:new{
|
||||
menu_title = "Fonts Menu",
|
||||
item_array = FontChooser.fonts,
|
||||
}
|
||||
FontChooser.cfont = FontChooser.fonts[fonts_menu:choose(0, height)]
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_S then -- invoke search input
|
||||
keywords = InputBox:input(height-100, 100, "Search:")
|
||||
if keywords then -- display search result according to keywords
|
||||
--[[
|
||||
----------------------------------------------------------------
|
||||
|| uncomment following line and set the correct path if you want
|
||||
|| to test search feature in EMU mode
|
||||
----------------------------------------------------------------
|
||||
--]]
|
||||
--FileSearcher:init("/home/dave/documents/kindle/backup/documents")
|
||||
FileSearcher:init()
|
||||
file = FileSearcher:choose(ypos, height, keywords)
|
||||
if file then
|
||||
return file
|
||||
end
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
|
||||
266
filesearcher.lua
Normal file
266
filesearcher.lua
Normal file
@@ -0,0 +1,266 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "fontchooser"
|
||||
|
||||
FileSearcher = {
|
||||
-- font for displaying toc item names
|
||||
fsize = 25,
|
||||
face = nil,
|
||||
fhash = nil,
|
||||
-- font for page title
|
||||
tfsize = 30,
|
||||
tface = nil,
|
||||
tfhash = nil,
|
||||
-- font for paging display
|
||||
ffsize = 16,
|
||||
fface = nil,
|
||||
ffhash = nil,
|
||||
|
||||
-- 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
|
||||
if lfs.attributes(self.path.."/"..f, "mode") == "directory"
|
||||
and f ~= "." and f~= ".." and not string.match(f, "^%.[^.]") then
|
||||
table.insert(new_dirs, d.."/"..f)
|
||||
elseif string.match(f, ".+%.[pP][dD][fF]$") then
|
||||
file_entry = {dir=d, name=f,}
|
||||
table.insert(self.files, file_entry)
|
||||
--print("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:updateFont()
|
||||
if self.fhash ~= FontChooser.cfont..self.fsize then
|
||||
self.face = freetype.newBuiltinFace(FontChooser.cfont, self.fsize)
|
||||
self.fhash = FontChooser.cfont..self.fsize
|
||||
end
|
||||
|
||||
if self.tfhash ~= FontChooser.tfont..self.tfsize then
|
||||
self.tface = freetype.newBuiltinFace(FontChooser.tfont, self.tfsize)
|
||||
self.tfhash = FontChooser.tfont..self.tfsize
|
||||
end
|
||||
|
||||
if self.ffhash ~= FontChooser.ffont..self.ffsize then
|
||||
self.fface = freetype.newBuiltinFace(FontChooser.ffont, self.ffsize)
|
||||
self.ffhash = FontChooser.ffont..self.ffsize
|
||||
end
|
||||
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.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
|
||||
end
|
||||
|
||||
|
||||
function FileSearcher:choose(ypos, height, keywords)
|
||||
local perpage = math.floor(height / self.spacing) - 2
|
||||
local pagedirty = true
|
||||
local markerdirty = false
|
||||
self:updateFont()
|
||||
|
||||
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
|
||||
|
||||
-- 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
|
||||
if pagedirty then
|
||||
markerdirty = true
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
|
||||
-- draw menu title
|
||||
renderUtf8Text(fb.bb, 30, ypos + self.title_H, self.tface, self.tfhash,
|
||||
"Search Result for: "..keywords, true)
|
||||
|
||||
-- draw results
|
||||
local c
|
||||
if self.items == 0 then -- nothing found
|
||||
y = ypos + self.title_H + self.spacing * 2
|
||||
renderUtf8Text(fb.bb, 20, y, self.face, self.fhash,
|
||||
"Sorry, no match found.", true)
|
||||
renderUtf8Text(fb.bb, 20, y + self.spacing, self.face, self.fhash,
|
||||
"Please try a different keyword.", true)
|
||||
markerdirty = false
|
||||
else -- found something, draw it
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 50, y, self.face, self.fhash,
|
||||
self.result[i].name, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- draw footer
|
||||
y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
all_page = (math.floor(self.items / perpage)+1)
|
||||
renderUtf8Text(fb.bb, x, y, self.fface, self.ffhash,
|
||||
"Page "..self.page.." of "..all_page, true)
|
||||
end
|
||||
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 30, y, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = ypos + self.title_H + (self.spacing * self.current) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
fb:refresh(1, 30, y, 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.waitForEvent()
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
ev.code = adjustFWKey(ev.code)
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_PGFWD 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 then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
else
|
||||
self.current = 1
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_S then
|
||||
old_keywords = keywords
|
||||
keywords = InputBox:input(height-100, 100, "Search:", old_keywords)
|
||||
if keywords then
|
||||
self:setSearchResult(keywords)
|
||||
else
|
||||
keywords = old_keywords
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
file_entry = self.result[perpage*(self.page-1)+self.current]
|
||||
file_path = file_entry.dir .. "/" .. file_entry.name
|
||||
|
||||
if PDFReader:open(file_path,"") then -- TODO: query for password
|
||||
PDFReader:goto(tonumber(PDFReader.settings:readsetting("last_page") or 1))
|
||||
PDFReader:inputloop()
|
||||
end
|
||||
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_BACK then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
147
fontchooser.lua
147
fontchooser.lua
@@ -1,153 +1,20 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
|
||||
FontChooser = {
|
||||
-- font for displaying file/dir names
|
||||
face = freetype.newBuiltinFace("sans", 25),
|
||||
fhash = "s25",
|
||||
-- font for page title
|
||||
tface = freetype.newBuiltinFace("Helvetica-BoldOblique", 32),
|
||||
tfhash = "hbo32",
|
||||
-- font for paging display
|
||||
sface = freetype.newBuiltinFace("sans", 16),
|
||||
sfhash = "s16",
|
||||
-- title height
|
||||
title_H = 45,
|
||||
-- spacing between lines
|
||||
spacing = 40,
|
||||
-- foot height
|
||||
foot_H = 27,
|
||||
-- font name for menu contents
|
||||
cfont = "sans",
|
||||
-- font name for title
|
||||
tfont = "Helvetica-BoldOblique",
|
||||
-- font name for footer
|
||||
ffont = "sans",
|
||||
|
||||
-- state buffer
|
||||
fonts = {"sans", "cjk", "mono",
|
||||
"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
|
||||
"Helvetica", "Helvetica-Oblique", "Helvetica-BoldOblique",
|
||||
"Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic",},
|
||||
items = 14,
|
||||
page = 1,
|
||||
current = 2,
|
||||
oldcurrent = 1,
|
||||
}
|
||||
|
||||
function FontChooser:init()
|
||||
self.items = #self.fonts
|
||||
table.sort(self.fonts)
|
||||
clearglyphcache()
|
||||
end
|
||||
|
||||
|
||||
function FontChooser: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
|
||||
if pagedirty then
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), height, 0)
|
||||
|
||||
-- draw menu title
|
||||
renderUtf8Text(fb.bb, 30, ypos + self.title_H, self.tface, self.tfhash,
|
||||
"[ Fonts Menu ]", true)
|
||||
|
||||
local c
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 50, y, self.face, self.fhash, self.fonts[i], true)
|
||||
end
|
||||
end
|
||||
y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
renderUtf8Text(fb.bb, x, y, self.sface, self.sfhash,
|
||||
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
|
||||
markerdirty = true
|
||||
end
|
||||
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 30, y, fb.bb:getWidth() - 60, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = ypos + self.title_H + (self.spacing * self.current) + 10
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
fb:refresh(1, 30, y, 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.waitForEvent()
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_PGFWD 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 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 newface = self.fonts[perpage*(self.page-1)+self.current]
|
||||
return newface
|
||||
elseif ev.code == KEY_BACK then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
209
inputbox.lua
Normal file
209
inputbox.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
|
||||
InputBox = {
|
||||
-- Class vars:
|
||||
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,
|
||||
|
||||
-- font for displaying input content
|
||||
face = freetype.newBuiltinFace("mono", 25),
|
||||
fhash = "m25",
|
||||
fheight = 25,
|
||||
fwidth = 16,
|
||||
}
|
||||
|
||||
function InputBox:setDefaultInput(text)
|
||||
self.input_string = ""
|
||||
self:addString(text)
|
||||
--renderUtf8Text(fb.bb, self.input_start_x, self.input_start_y,
|
||||
--self.face, self.fhash, text, true)
|
||||
--self.input_cur_x = self.input_start_x + (string.len(text) * self.fwidth)
|
||||
--self.input_string = text
|
||||
end
|
||||
|
||||
function InputBox:addString(str)
|
||||
for i = 1, #str do
|
||||
self:addChar(str:sub(i,i))
|
||||
end
|
||||
end
|
||||
|
||||
function InputBox:addChar(char)
|
||||
renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, self.face, self.fhash,
|
||||
char, true)
|
||||
fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight)
|
||||
self.input_cur_x = self.input_cur_x + self.fwidth
|
||||
self.input_string = self.input_string .. char
|
||||
end
|
||||
|
||||
function InputBox:delChar()
|
||||
if self.input_start_x == self.input_cur_x then
|
||||
return
|
||||
end
|
||||
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)
|
||||
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, self.fhash,
|
||||
title, true)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
|| d_text default to nil (used to set default text in input slot)
|
||||
--]]
|
||||
function InputBox:input(ypos, height, title, d_text)
|
||||
local pagedirty = true
|
||||
-- do some initilization
|
||||
self.input_start_y = ypos + 35
|
||||
self.input_cur_x = self.input_start_x
|
||||
|
||||
if d_text then -- if specified default text, draw it
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
self:setDefaultInput(d_text)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
else -- otherwise, leave the draw task to the main loop
|
||||
self.input_string = ""
|
||||
end
|
||||
|
||||
while true do
|
||||
if pagedirty then
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
ev.code = adjustFWKey(ev.code)
|
||||
--local secs, usecs = util.gettime()
|
||||
if ev.code == KEY_SHIFT then
|
||||
self.shiftmode = true
|
||||
elseif ev.code == KEY_ALT then
|
||||
self.altmode = true
|
||||
elseif ev.code == KEY_FW_UP then
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
elseif ev.code == KEY_A then
|
||||
self:addChar("a")
|
||||
elseif ev.code == KEY_B then
|
||||
self:addChar("b")
|
||||
elseif ev.code == KEY_C then
|
||||
self:addChar("c")
|
||||
elseif ev.code == KEY_D then
|
||||
self:addChar("d")
|
||||
elseif ev.code == KEY_E then
|
||||
self:addChar("e")
|
||||
elseif ev.code == KEY_F then
|
||||
self:addChar("f")
|
||||
elseif ev.code == KEY_G then
|
||||
self:addChar("g")
|
||||
elseif ev.code == KEY_H then
|
||||
self:addChar("h")
|
||||
elseif ev.code == KEY_I then
|
||||
self:addChar("i")
|
||||
elseif ev.code == KEY_J then
|
||||
self:addChar("j")
|
||||
elseif ev.code == KEY_K then
|
||||
self:addChar("k")
|
||||
elseif ev.code == KEY_L then
|
||||
self:addChar("l")
|
||||
elseif ev.code == KEY_M then
|
||||
self:addChar("m")
|
||||
elseif ev.code == KEY_N then
|
||||
self:addChar("n")
|
||||
elseif ev.code == KEY_O then
|
||||
self:addChar("o")
|
||||
elseif ev.code == KEY_P then
|
||||
self:addChar("p")
|
||||
elseif ev.code == KEY_Q then
|
||||
self:addChar("q")
|
||||
elseif ev.code == KEY_R then
|
||||
self:addChar("r")
|
||||
elseif ev.code == KEY_S then
|
||||
self:addChar("s")
|
||||
elseif ev.code == KEY_T then
|
||||
self:addChar("t")
|
||||
elseif ev.code == KEY_U then
|
||||
self:addChar("u")
|
||||
elseif ev.code == KEY_V then
|
||||
self:addChar("v")
|
||||
elseif ev.code == KEY_W then
|
||||
self:addChar("w")
|
||||
elseif ev.code == KEY_X then
|
||||
self:addChar("x")
|
||||
elseif ev.code == KEY_Y then
|
||||
self:addChar("y")
|
||||
elseif ev.code == KEY_Z then
|
||||
self:addChar("z")
|
||||
elseif ev.code == KEY_1 then
|
||||
self:addChar("1")
|
||||
elseif ev.code == KEY_2 then
|
||||
self:addChar("2")
|
||||
elseif ev.code == KEY_3 then
|
||||
self:addChar("3")
|
||||
elseif ev.code == KEY_4 then
|
||||
self:addChar("4")
|
||||
elseif ev.code == KEY_5 then
|
||||
self:addChar("5")
|
||||
elseif ev.code == KEY_6 then
|
||||
self:addChar("6")
|
||||
elseif ev.code == KEY_7 then
|
||||
self:addChar("7")
|
||||
elseif ev.code == KEY_8 then
|
||||
self:addChar("8")
|
||||
elseif ev.code == KEY_9 then
|
||||
self:addChar("9")
|
||||
elseif ev.code == KEY_0 then
|
||||
self:addChar("0")
|
||||
elseif ev.code == KEY_SPACE then
|
||||
self:addChar(" ")
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
if self.input_string == "" then
|
||||
return nil
|
||||
else
|
||||
return self.input_string
|
||||
end
|
||||
elseif ev.code == KEY_DEL then
|
||||
self:delChar()
|
||||
elseif ev.code == KEY_BACK then
|
||||
return nil
|
||||
end
|
||||
|
||||
--local nsecs, nusecs = util.gettime()
|
||||
--local dur = (nsecs - secs) * 1000000 + nusecs - usecs
|
||||
--print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur)
|
||||
elseif ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_RELEASE
|
||||
and ev.code == KEY_SHIFT then
|
||||
self.shiftmode = false
|
||||
elseif ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_RELEASE
|
||||
and ev.code == KEY_ALT then
|
||||
self.altmode = false
|
||||
end
|
||||
end
|
||||
end
|
||||
97
keys.lua
97
keys.lua
@@ -109,25 +109,120 @@ function set_emu_keycodes()
|
||||
KEY_PGFWD = 117
|
||||
KEY_PGBCK = 112
|
||||
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 = 36 -- enter for now
|
||||
KEY_SPACE = 65
|
||||
|
||||
KEY_ENTER = 36
|
||||
|
||||
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_SHIFT = 50 -- left shift
|
||||
KEY_ALT = 64 -- left alt
|
||||
KEY_VPLUS = 95 -- F11
|
||||
KEY_VMINUS = 96 -- F12
|
||||
end
|
||||
|
||||
function getRotationMode()
|
||||
--[[
|
||||
return code for four kinds of rotation mode:
|
||||
|
||||
0 for no rotation,
|
||||
1 for landscape with bottom on the right side of screen, etc.
|
||||
|
||||
2
|
||||
-----------
|
||||
| ------- |
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
3 | | | | 1
|
||||
| | | |
|
||||
| | | |
|
||||
| ------- |
|
||||
| |
|
||||
-----------
|
||||
0
|
||||
--]]
|
||||
if KEY_FW_DOWN == 116 then -- in EMU mode always return 0
|
||||
return 0
|
||||
end
|
||||
orie_fd = assert(io.open("/sys/module/eink_fb_hal_broads/parameters/bs_orientation", "r"))
|
||||
updown_fd = assert(io.open("/sys/module/eink_fb_hal_broads/parameters/bs_upside_down", "r"))
|
||||
mode = orie_fd:read() + (updown_fd:read() * 2)
|
||||
return mode
|
||||
end
|
||||
|
||||
function adjustFWKey(code)
|
||||
if getRotationMode() == 0 then
|
||||
return code
|
||||
elseif getRotationMode() == 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 getRotationMode() == 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 getRotationMode() == 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
|
||||
end
|
||||
|
||||
51
pdf.c
51
pdf.c
@@ -87,6 +87,56 @@ static int getNumberOfPages(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function for getTableOfContent()
|
||||
*/
|
||||
static int walkTableOfContent(lua_State *L, fz_outline* ol, int *count, int depth) {
|
||||
depth++;
|
||||
while(ol) {
|
||||
lua_pushnumber(L, *count);
|
||||
|
||||
/* set subtable */
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "page");
|
||||
lua_pushnumber(L, ol->dest.ld.gotor.page + 1);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "depth");
|
||||
lua_pushnumber(L, depth);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "title");
|
||||
lua_pushstring(L, ol->title);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_settable(L, -3);
|
||||
|
||||
(*count)++;
|
||||
if (ol->down) {
|
||||
walkTableOfContent(L, ol->down, count, depth);
|
||||
}
|
||||
ol = ol->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a table like this:
|
||||
* {
|
||||
* {page=12, depth=1, title="chapter1"},
|
||||
* {page=54, depth=1, title="chapter2"},
|
||||
* }
|
||||
*/
|
||||
static int getTableOfContent(lua_State *L) {
|
||||
fz_outline *ol;
|
||||
int count = 1;
|
||||
|
||||
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
||||
ol = pdf_load_outline(doc->xref);
|
||||
|
||||
lua_newtable(L);
|
||||
walkTableOfContent(L, ol, &count, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int newDrawContext(lua_State *L) {
|
||||
int rotate = luaL_optint(L, 1, 0);
|
||||
double zoom = luaL_optnumber(L, 2, (double) 1.0);
|
||||
@@ -309,6 +359,7 @@ static const struct luaL_reg pdf_func[] = {
|
||||
static const struct luaL_reg pdfdocument_meth[] = {
|
||||
{"openPage", openPage},
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getTOC", getTableOfContent},
|
||||
{"close", closeDocument},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
|
||||
106
pdfreader.lua
106
pdfreader.lua
@@ -1,5 +1,7 @@
|
||||
require "keys"
|
||||
require "settings"
|
||||
--require "tocmenu"
|
||||
require "selectmenu"
|
||||
|
||||
PDFReader = {
|
||||
-- "constants":
|
||||
@@ -58,6 +60,7 @@ PDFReader = {
|
||||
-- tile cache state:
|
||||
cache_current_memsize = 0,
|
||||
cache = {},
|
||||
jump_stack = {},
|
||||
}
|
||||
|
||||
-- guarantee that we have enough memory in cache
|
||||
@@ -234,6 +237,35 @@ function PDFReader:goto(no)
|
||||
if no < 1 or no > self.doc:getPages() then
|
||||
return
|
||||
end
|
||||
|
||||
-- for jump_stack
|
||||
if self.pageno and math.abs(self.pageno - no) > 1 then
|
||||
local jump_item = nil
|
||||
-- add current page to jump_stack if no in
|
||||
for _t,_v in ipairs(self.jump_stack) do
|
||||
if _v.page == self.pageno then
|
||||
jump_item = _v
|
||||
table.remove(self.jump_stack, _t)
|
||||
elseif _v.page == no then
|
||||
-- the page we jumped to should not be show in stack
|
||||
table.remove(self.jump_stack, _t)
|
||||
end
|
||||
end
|
||||
-- create a new one if not found
|
||||
if not jump_item then
|
||||
jump_item = {
|
||||
page = self.pageno,
|
||||
datetime = os.date("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
end
|
||||
-- insert at the start
|
||||
table.insert(self.jump_stack, 1, jump_item)
|
||||
if #self.jump_stack > 10 then
|
||||
-- remove the last element to keep the size less than 10
|
||||
table.remove(self.jump_stack)
|
||||
end
|
||||
end
|
||||
|
||||
self.pageno = no
|
||||
self:show(no)
|
||||
if no < self.doc:getPages() then
|
||||
@@ -275,6 +307,48 @@ function PDFReader:setrotate(rotate)
|
||||
self:goto(self.pageno)
|
||||
end
|
||||
|
||||
function PDFReader:showTOC()
|
||||
toc = self.doc:getTOC()
|
||||
local menu_items = {}
|
||||
-- build menu items
|
||||
for _k,_v in ipairs(toc) do
|
||||
table.insert(menu_items,
|
||||
(" "):rep(_v.depth-1).._v.title)
|
||||
end
|
||||
toc_menu = SelectMenu:new{
|
||||
menu_title = "Table of Contents",
|
||||
item_array = menu_items,
|
||||
no_item_msg = "This document does not have a Table of Contents.",
|
||||
}
|
||||
item_no = toc_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no then
|
||||
self:goto(toc[item_no].page)
|
||||
else
|
||||
self:goto(self.pageno)
|
||||
end
|
||||
end
|
||||
|
||||
function PDFReader:showJumpStack()
|
||||
local menu_items = {}
|
||||
for _k,_v in ipairs(self.jump_stack) do
|
||||
table.insert(menu_items,
|
||||
_v.datetime.." -> Page ".._v.page)
|
||||
end
|
||||
jump_menu = SelectMenu:new{
|
||||
menu_title = "Jump Keeper (current page: "..self.pageno..")",
|
||||
item_array = menu_items,
|
||||
no_item_msg = "No jump history.",
|
||||
}
|
||||
item_no = jump_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no then
|
||||
local jump_item = self.jump_stack[item_no]
|
||||
self:goto(jump_item.page)
|
||||
else
|
||||
self:goto(self.pageno)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- wait for input and handle it
|
||||
function PDFReader:inputloop()
|
||||
while 1 do
|
||||
@@ -302,16 +376,24 @@ function PDFReader:inputloop()
|
||||
self:goto(self.pageno - 1)
|
||||
end
|
||||
elseif ev.code == KEY_BACK then
|
||||
self:clearcache()
|
||||
if self.doc ~= nil then
|
||||
self.doc:close()
|
||||
if self.altmode then
|
||||
-- in altmode, back to last jump
|
||||
if #self.jump_stack ~= 0 then
|
||||
self:goto(self.jump_stack[1].page)
|
||||
end
|
||||
else
|
||||
-- not shiftmode, exit pdfreader
|
||||
self:clearcache()
|
||||
if self.doc ~= nil then
|
||||
self.doc:close()
|
||||
end
|
||||
if self.settings ~= nil then
|
||||
self.settings:savesetting("last_page", self.pageno)
|
||||
self.settings:savesetting("gamma", self.globalgamma)
|
||||
self.settings:close()
|
||||
end
|
||||
return
|
||||
end
|
||||
if self.settings ~= nil then
|
||||
self.settings:savesetting("last_page", self.pageno)
|
||||
self.settings:savesetting("gamma", self.globalgamma)
|
||||
self.settings:close()
|
||||
end
|
||||
return
|
||||
elseif ev.code == KEY_VPLUS then
|
||||
self:modify_gamma( 1.25 )
|
||||
elseif ev.code == KEY_VMINUS then
|
||||
@@ -334,7 +416,10 @@ function PDFReader:inputloop()
|
||||
else
|
||||
self:setglobalzoommode(self.ZOOM_FIT_TO_PAGE_HEIGHT)
|
||||
end
|
||||
|
||||
elseif ev.code == KEY_T then
|
||||
self:showTOC()
|
||||
elseif ev.code == KEY_B then
|
||||
self:showJumpStack()
|
||||
elseif ev.code == KEY_J then
|
||||
self:setrotate( self.globalrotate + 10 )
|
||||
elseif ev.code == KEY_K then
|
||||
@@ -401,6 +486,7 @@ function PDFReader:inputloop()
|
||||
local dur = (nsecs - secs) * 1000000 + nusecs - usecs
|
||||
print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur)
|
||||
elseif ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_RELEASE and ev.code == KEY_SHIFT then
|
||||
print "shift haha"
|
||||
self.shiftmode = false
|
||||
elseif ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_RELEASE and ev.code == KEY_ALT then
|
||||
self.altmode = false
|
||||
|
||||
16
reader.lua
16
reader.lua
@@ -20,6 +20,7 @@
|
||||
require "alt_getopt"
|
||||
require "pdfreader"
|
||||
require "filechooser"
|
||||
require "settings"
|
||||
|
||||
-- option parsing:
|
||||
longopts = {
|
||||
@@ -82,6 +83,14 @@ end
|
||||
fb = einkfb.open("/dev/fb0")
|
||||
width, height = fb:getSize()
|
||||
|
||||
-- set up reader's setting: font
|
||||
reader_settings = DocSettings:open(".reader")
|
||||
r_cfont = reader_settings:readsetting("cfont")
|
||||
if r_cfont ~=nil then
|
||||
FontChooser.cfont = r_cfont
|
||||
end
|
||||
|
||||
|
||||
if lfs.attributes(ARGV[optind], "mode") == "directory" then
|
||||
local running = true
|
||||
FileChooser:setPath(ARGV[optind])
|
||||
@@ -101,3 +110,10 @@ else
|
||||
PDFReader:goto(tonumber(optarg["g"]) or tonumber(PDFReader.settings:readsetting("last_page") or 1))
|
||||
PDFReader:inputloop()
|
||||
end
|
||||
|
||||
-- save reader settings
|
||||
reader_settings:savesetting("cfont", FontChooser.cfont)
|
||||
reader_settings:close()
|
||||
|
||||
input.closeAll()
|
||||
os.execute('test -e /proc/keypad && echo "send '..KEY_HOME..'" > /proc/keypad ')
|
||||
|
||||
206
selectmenu.lua
Normal file
206
selectmenu.lua
Normal file
@@ -0,0 +1,206 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "fontchooser"
|
||||
|
||||
SelectMenu = {
|
||||
-- font for displaying item names
|
||||
fsize = 22,
|
||||
face = nil,
|
||||
fhash = nil,
|
||||
-- font for page title
|
||||
tfsize = 25,
|
||||
tface = nil,
|
||||
tfhash = nil,
|
||||
-- font for paging display
|
||||
ffsize = 16,
|
||||
fface = nil,
|
||||
ffhash = nil,
|
||||
|
||||
-- title height
|
||||
title_H = 40,
|
||||
-- spacing between lines
|
||||
spacing = 36,
|
||||
-- foot height
|
||||
foot_H = 27,
|
||||
|
||||
menu_title = "None Titled",
|
||||
no_item_msg = "No items found.",
|
||||
item_array = {},
|
||||
items = 14,
|
||||
|
||||
-- state buffer
|
||||
page = 1,
|
||||
current = 1,
|
||||
oldcurrent = 0,
|
||||
}
|
||||
|
||||
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
|
||||
return o
|
||||
end
|
||||
|
||||
function SelectMenu:updateFont()
|
||||
if self.fhash ~= FontChooser.cfont..self.fsize then
|
||||
self.face = freetype.newBuiltinFace(FontChooser.cfont, self.fsize)
|
||||
self.fhash = FontChooser.cfont..self.fsize
|
||||
end
|
||||
|
||||
if self.tfhash ~= FontChooser.tfont..self.tfsize then
|
||||
self.tface = freetype.newBuiltinFace(FontChooser.tfont, self.tfsize)
|
||||
self.tfhash = FontChooser.tfont..self.tfsize
|
||||
end
|
||||
|
||||
if self.ffhash ~= FontChooser.ffont..self.ffsize then
|
||||
self.fface = freetype.newBuiltinFace(FontChooser.ffont, self.ffsize)
|
||||
self.ffhash = FontChooser.ffont..self.ffsize
|
||||
end
|
||||
end
|
||||
|
||||
--[
|
||||
-- return the index of selected item
|
||||
--]
|
||||
function SelectMenu:choose(ypos, height)
|
||||
local perpage = math.floor(height / self.spacing) - 2
|
||||
local pagedirty = true
|
||||
local markerdirty = false
|
||||
self:updateFont()
|
||||
|
||||
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
|
||||
if pagedirty then
|
||||
markerdirty = true
|
||||
-- draw menu title
|
||||
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), self.title_H + 10, 0)
|
||||
fb.bb:paintRect(30, ypos + 10, fb.bb:getWidth() - 60, self.title_H, 5)
|
||||
--x = fb.bb:getWidth() - 260 -- move text to the right
|
||||
x = 40
|
||||
y = ypos + self.title_H
|
||||
renderUtf8Text(fb.bb, x, y, self.tface, self.tfhash,
|
||||
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, self.face, self.fhash,
|
||||
"Oops... Bad news for you:", true)
|
||||
y = y + self.spacing
|
||||
renderUtf8Text(fb.bb, 30, y, self.face, self.fhash,
|
||||
self.no_item_msg, true)
|
||||
markerdirty = false
|
||||
else
|
||||
local c
|
||||
for c = 1, perpage do
|
||||
local i = (self.page - 1) * perpage + c
|
||||
if i <= self.items then
|
||||
y = ypos + self.title_H + (self.spacing * c)
|
||||
renderUtf8Text(fb.bb, 30, y, self.face, self.fhash,
|
||||
self.item_array[i], true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- draw footer
|
||||
y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H + 5
|
||||
x = (fb.bb:getWidth() / 2) - 50
|
||||
renderUtf8Text(fb.bb, x, y, self.fface, self.ffhash,
|
||||
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
|
||||
end
|
||||
|
||||
if markerdirty then
|
||||
if not pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = ypos + self.title_H + (self.spacing * self.oldcurrent) + 8
|
||||
fb.bb:paintRect(30, y, fb.bb:getWidth() - 60, 3, 0)
|
||||
fb:refresh(1, 30, 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(30, y, fb.bb:getWidth() - 60, 3, 15)
|
||||
if not pagedirty then
|
||||
fb:refresh(1, 30, y, 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.waitForEvent()
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
ev.code = adjustFWKey(ev.code)
|
||||
if ev.code == KEY_FW_UP then
|
||||
prevItem()
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
nextItem()
|
||||
elseif ev.code == KEY_PGFWD 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 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
|
||||
if self.items == 0 then
|
||||
return nil
|
||||
else
|
||||
return (perpage*(self.page-1) + self.current)
|
||||
end
|
||||
elseif ev.code == KEY_BACK then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user