diff --git a/pdfreader.lua b/pdfreader.lua index f358f248b..9570e651f 100644 --- a/pdfreader.lua +++ b/pdfreader.lua @@ -1,6 +1,7 @@ require "keys" require "settings" require "tocmenu" +require "selectmenu" PDFReader = { -- "constants": @@ -59,6 +60,7 @@ PDFReader = { -- tile cache state: cache_current_memsize = 0, cache = {}, + jump_stack = {}, } -- guarantee that we have enough memory in cache @@ -235,6 +237,36 @@ 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 + print('@add: '..jump_item.page..", current: "..self.pageno) + end + self.pageno = no self:show(no) if no < self.doc:getPages() then @@ -337,15 +369,34 @@ function PDFReader:inputloop() end elseif ev.code == KEY_T then - -- show table of content menu - toc = self.doc:getTOC() - toc_menu = TOCMenu:new(toc) - --toc_menu:dump() - no = toc_menu:choose(0, fb.bb:getHeight()) - if no then - self:goto(no) + if self.altmode then + -- show jump_stack + 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( + "Jump Keeper, Current page: "..self.pageno, menu_items) + jump_re = jump_menu:choose(0, fb.bb:getHeight()) + jump_menu = nil + if jump_re then + local jump_item = self.jump_stack[jump_re] + self:goto(jump_item.page) + else + self:goto(self.pageno) + end else - self:goto(self.pageno) + -- show table of content menu + toc = self.doc:getTOC() + toc_menu = TOCMenu:new(toc) + --toc_menu:dump() + no = toc_menu:choose(0, fb.bb:getHeight()) + if no then + self:goto(no) + else + self:goto(self.pageno) + end end elseif ev.code == KEY_J then diff --git a/selectmenu.lua b/selectmenu.lua new file mode 100644 index 000000000..7d6daf5d6 --- /dev/null +++ b/selectmenu.lua @@ -0,0 +1,203 @@ +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, + + -- state buffer + menu_title = "None Title", + item_array = {}, + items = 14, + page = 1, + current = 1, + oldcurrent = 0, +} + +function SelectMenu:new(menu_title, item_array) + instance = self + instance.item_array = item_array + instance.menu_title = menu_title + instance.items = #item_array + instance.current = 1 + instance.oldcurrent = 0 + return instance +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, + "No items found.", 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