From 9e531fc2dbb860de557c834e1056a23870d5bc96 Mon Sep 17 00:00:00 2001 From: HW Date: Sat, 19 May 2012 01:10:57 +0200 Subject: [PATCH] file reorganisation all lua frontend files are now in the frontend/ directory. all old code is cleaned up. --- commands.lua | 187 -- crereader.lua | 456 ---- djvureader.lua | 98 - filechooser.lua | 243 -- filesearcher.lua | 318 --- alt_getopt.lua => frontend/alt_getopt.lua | 0 cache.lua => frontend/cache.lua | 0 .../document/document.lua | 2 +- settings.lua => frontend/settings.lua | 0 dialog.lua => frontend/ui/dialog.lua | 5 +- event.lua => frontend/ui/event.lua | 0 font.lua => frontend/ui/font.lua | 1 + geometry.lua => frontend/ui/geometry.lua | 0 graphics.lua => frontend/ui/graphics.lua | 0 image.lua => frontend/ui/image.lua | 0 inputevent.lua => frontend/ui/inputevent.lua | 2 +- .../ui/reader/readerpaging.lua | 0 .../ui/reader/readerpanning.lua | 0 .../ui/reader/readerrotation.lua | 0 .../ui/reader/readerview.lua | 2 - .../ui/reader/readerzooming.lua | 0 readerui.lua => frontend/ui/readerui.lua | 12 +- rendertext.lua => frontend/ui/rendertext.lua | 0 screen.lua => frontend/ui/screen.lua | 0 ui.lua => frontend/ui/ui.lua | 10 +- widget.lua => frontend/ui/widget.lua | 12 +- helppage.lua | 128 - inputbox.lua | 402 --- keys.lua | 278 --- pdfreader.lua | 47 - reader.lua | 179 -- selectmenu.lua | 343 --- unireader.lua | 2221 ----------------- wtest.lua | 8 +- 34 files changed, 27 insertions(+), 4927 deletions(-) delete mode 100644 commands.lua delete mode 100644 crereader.lua delete mode 100644 djvureader.lua delete mode 100644 filechooser.lua delete mode 100644 filesearcher.lua rename alt_getopt.lua => frontend/alt_getopt.lua (100%) rename cache.lua => frontend/cache.lua (100%) rename document.lua => frontend/document/document.lua (99%) rename settings.lua => frontend/settings.lua (100%) rename dialog.lua => frontend/ui/dialog.lua (99%) rename event.lua => frontend/ui/event.lua (100%) rename font.lua => frontend/ui/font.lua (98%) rename geometry.lua => frontend/ui/geometry.lua (100%) rename graphics.lua => frontend/ui/graphics.lua (100%) rename image.lua => frontend/ui/image.lua (100%) rename inputevent.lua => frontend/ui/inputevent.lua (99%) rename readerpaging.lua => frontend/ui/reader/readerpaging.lua (100%) rename readerpanning.lua => frontend/ui/reader/readerpanning.lua (100%) rename readerrotation.lua => frontend/ui/reader/readerrotation.lua (100%) rename readerview.lua => frontend/ui/reader/readerview.lua (99%) rename readerzooming.lua => frontend/ui/reader/readerzooming.lua (100%) rename readerui.lua => frontend/ui/readerui.lua (89%) rename rendertext.lua => frontend/ui/rendertext.lua (100%) rename screen.lua => frontend/ui/screen.lua (100%) rename ui.lua => frontend/ui/ui.lua (98%) rename widget.lua => frontend/ui/widget.lua (98%) delete mode 100644 helppage.lua delete mode 100644 inputbox.lua delete mode 100644 keys.lua delete mode 100644 pdfreader.lua delete mode 100755 reader.lua delete mode 100644 selectmenu.lua delete mode 100644 unireader.lua diff --git a/commands.lua b/commands.lua deleted file mode 100644 index 83e2381cf..000000000 --- a/commands.lua +++ /dev/null @@ -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 "") -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 diff --git a/crereader.lua b/crereader.lua deleted file mode 100644 index d59a35722..000000000 --- a/crereader.lua +++ /dev/null @@ -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 *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 diff --git a/djvureader.lua b/djvureader.lua deleted file mode 100644 index 6bb7a89cd..000000000 --- a/djvureader.lua +++ /dev/null @@ -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 - - diff --git a/filechooser.lua b/filechooser.lua deleted file mode 100644 index 0e4ff59a2..000000000 --- a/filechooser.lua +++ /dev/null @@ -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 diff --git a/filesearcher.lua b/filesearcher.lua deleted file mode 100644 index f36d14152..000000000 --- a/filesearcher.lua +++ /dev/null @@ -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 diff --git a/alt_getopt.lua b/frontend/alt_getopt.lua similarity index 100% rename from alt_getopt.lua rename to frontend/alt_getopt.lua diff --git a/cache.lua b/frontend/cache.lua similarity index 100% rename from cache.lua rename to frontend/cache.lua diff --git a/document.lua b/frontend/document/document.lua similarity index 99% rename from document.lua rename to frontend/document/document.lua index 546cf82d8..dc047cb8e 100644 --- a/document.lua +++ b/frontend/document/document.lua @@ -95,7 +95,7 @@ end require "cache" -require "geometry" +require "ui/geometry" PdfDocument = Document:new{ diff --git a/settings.lua b/frontend/settings.lua similarity index 100% rename from settings.lua rename to frontend/settings.lua diff --git a/dialog.lua b/frontend/ui/dialog.lua similarity index 99% rename from dialog.lua rename to frontend/ui/dialog.lua index 6594d4a1c..3e679b426 100644 --- a/dialog.lua +++ b/frontend/ui/dialog.lua @@ -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 diff --git a/event.lua b/frontend/ui/event.lua similarity index 100% rename from event.lua rename to frontend/ui/event.lua diff --git a/font.lua b/frontend/ui/font.lua similarity index 98% rename from font.lua rename to frontend/ui/font.lua index d294d8bf4..f9d77b639 100644 --- a/font.lua +++ b/frontend/ui/font.lua @@ -1,3 +1,4 @@ +require "settings" -- for debug() Font = { fontmap = { diff --git a/geometry.lua b/frontend/ui/geometry.lua similarity index 100% rename from geometry.lua rename to frontend/ui/geometry.lua diff --git a/graphics.lua b/frontend/ui/graphics.lua similarity index 100% rename from graphics.lua rename to frontend/ui/graphics.lua diff --git a/image.lua b/frontend/ui/image.lua similarity index 100% rename from image.lua rename to frontend/ui/image.lua diff --git a/inputevent.lua b/frontend/ui/inputevent.lua similarity index 99% rename from inputevent.lua rename to frontend/ui/inputevent.lua index 36a3f1b95..81bb67d80 100644 --- a/inputevent.lua +++ b/frontend/ui/inputevent.lua @@ -1,4 +1,4 @@ -require "event" +require "ui/event" -- constants from EV_KEY = 1 diff --git a/readerpaging.lua b/frontend/ui/reader/readerpaging.lua similarity index 100% rename from readerpaging.lua rename to frontend/ui/reader/readerpaging.lua diff --git a/readerpanning.lua b/frontend/ui/reader/readerpanning.lua similarity index 100% rename from readerpanning.lua rename to frontend/ui/reader/readerpanning.lua diff --git a/readerrotation.lua b/frontend/ui/reader/readerrotation.lua similarity index 100% rename from readerrotation.lua rename to frontend/ui/reader/readerrotation.lua diff --git a/readerview.lua b/frontend/ui/reader/readerview.lua similarity index 99% rename from readerview.lua rename to frontend/ui/reader/readerview.lua index 1ef5cde0a..bb6687ef4 100644 --- a/readerview.lua +++ b/frontend/ui/reader/readerview.lua @@ -1,5 +1,3 @@ -require "ui" - ReaderView = WidgetContainer:new{ document = nil, diff --git a/readerzooming.lua b/frontend/ui/reader/readerzooming.lua similarity index 100% rename from readerzooming.lua rename to frontend/ui/reader/readerzooming.lua diff --git a/readerui.lua b/frontend/ui/readerui.lua similarity index 89% rename from readerui.lua rename to frontend/ui/readerui.lua index 85bb53336..30b18d75d 100644 --- a/readerui.lua +++ b/frontend/ui/readerui.lua @@ -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 diff --git a/rendertext.lua b/frontend/ui/rendertext.lua similarity index 100% rename from rendertext.lua rename to frontend/ui/rendertext.lua diff --git a/screen.lua b/frontend/ui/screen.lua similarity index 100% rename from screen.lua rename to frontend/ui/screen.lua diff --git a/ui.lua b/frontend/ui/ui.lua similarity index 98% rename from ui.lua rename to frontend/ui/ui.lua index 662db0692..0120de519 100644 --- a/ui.lua +++ b/frontend/ui/ui.lua @@ -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 diff --git a/widget.lua b/frontend/ui/widget.lua similarity index 98% rename from widget.lua rename to frontend/ui/widget.lua index c8acdf004..5b814dc8f 100644 --- a/widget.lua +++ b/frontend/ui/widget.lua @@ -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 diff --git a/helppage.lua b/helppage.lua deleted file mode 100644 index 2bc374d28..000000000 --- a/helppage.lua +++ /dev/null @@ -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 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 diff --git a/inputbox.lua b/inputbox.lua deleted file mode 100644 index 34339841d..000000000 --- a/inputbox.lua +++ /dev/null @@ -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 diff --git a/keys.lua b/keys.lua deleted file mode 100644 index b9a436bfc..000000000 --- a/keys.lua +++ /dev/null @@ -1,278 +0,0 @@ ---[[ - This file contains settings related to key codes - - Copyright (C) 2011 Hans-Werner Hilse - - 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 . - - - - 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 -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 diff --git a/pdfreader.lua b/pdfreader.lua deleted file mode 100644 index 4d8ab1142..000000000 --- a/pdfreader.lua +++ /dev/null @@ -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 diff --git a/reader.lua b/reader.lua deleted file mode 100755 index b00fd5397..000000000 --- a/reader.lua +++ /dev/null @@ -1,179 +0,0 @@ -#!./kpdfview ---[[ - KindlePDFViewer: a reader implementation - Copyright (C) 2011 Hans-Werner Hilse - - 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 . -]]-- - -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 diff --git a/selectmenu.lua b/selectmenu.lua deleted file mode 100644 index dae914d0d..000000000 --- a/selectmenu.lua +++ /dev/null @@ -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 diff --git a/unireader.lua b/unireader.lua deleted file mode 100644 index 4ff82b109..000000000 --- a/unireader.lua +++ /dev/null @@ -1,2221 +0,0 @@ -require "keys" -require "settings" -require "selectmenu" -require "commands" -require "helppage" - -UniReader = { - -- "constants": - ZOOM_BY_VALUE = 0, - ZOOM_FIT_TO_PAGE = -1, - ZOOM_FIT_TO_PAGE_WIDTH = -2, - ZOOM_FIT_TO_PAGE_HEIGHT = -3, - ZOOM_FIT_TO_CONTENT = -4, - ZOOM_FIT_TO_CONTENT_WIDTH = -5, - ZOOM_FIT_TO_CONTENT_HEIGHT = -6, - ZOOM_FIT_TO_CONTENT_WIDTH_PAN = -7, - --ZOOM_FIT_TO_CONTENT_HEIGHT_PAN = -8, - ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN = -9, - ZOOM_FIT_TO_CONTENT_HALF_WIDTH = -10, - - GAMMA_NO_GAMMA = 1.0, - - -- framebuffer update policy state: - rcount = 5, - rcountmax = 5, - - -- zoom state: - globalzoom = 1.0, - globalzoom_orig = 1.0, - globalzoom_mode = -1, -- ZOOM_FIT_TO_PAGE - - globalrotate = 0, - - -- gamma setting: - globalgamma = 1.0, -- GAMMA_NO_GAMMA - - -- cached tile size - fullwidth = 0, - fullheight = 0, - -- size of current page for current zoom level in pixels - cur_full_width = 0, - cur_full_height = 0, - cur_bbox = {}, -- current page bbox - offset_x = 0, - offset_y = 0, - dest_x = 0, -- real offset_x when it's smaller than screen, so it's centered - dest_y = 0, - min_offset_x = 0, - min_offset_y = 0, - content_top = 0, -- for ZOOM_FIT_TO_CONTENT_WIDTH_PAN (prevView) - - -- set panning distance - shift_x = 100, - shift_y = 50, - pan_by_page = false, -- using shift_[xy] or width/height - pan_x = 0, -- top-left offset of page when pan activated - pan_y = 0, - pan_margin = 5, -- horizontal margin for two-column zoom (in pixels) - pan_overlap_vertical = 30, - show_overlap = 0, - - -- the document: - doc = nil, - -- the document's setting store: - settings = nil, - -- list of available commands: - commands = nil, - - -- we will use this one often, so keep it "static": - nulldc = DrawContext.new(), - - -- tile cache configuration: - cache_max_memsize = 1024*1024*5, -- 5MB tile cache - cache_max_ttl = 20, -- time to live - -- tile cache state: - cache_current_memsize = 0, - cache = {}, - -- renderer cache size - cache_document_size = 1024*1024*8, -- FIXME random, needs testing - - pagehash = nil, - - -- we use array to simluate two stacks, - -- one for backwards, one for forwards - jump_history = {cur = 1}, - bookmarks = {}, - highlight = {}, - toc = nil, - - bbox = {}, -- override getUsedBBox -} - -function UniReader:new(o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o -end - ----------------------------------------------------- --- !!!!!!!!!!!!!!!!!!!!!!!!! --- --- For a new specific reader, --- you must always overwrite following method: --- --- * self:open() --- --- overwrite other methods if needed. ----------------------------------------------------- - --- open a file -function UniReader:open(filename, cache_size) - return false -end - -function UniReader:init() - -- initialize commands - self:addAllCommands() -end - ----------------------------------------------------- --- highlight support ----------------------------------------------------- - -function UniReader:screenOffset() - local x = self.dest_x - local y = self.dest_y - if self.offset_x < 0 then - x = x + self.offset_x - end - if self.offset_y < 0 then - y = y + self.offset_y - end - debug("screenOffset", x, y) - return x,y -end - ----------------------------------------------------- --- Given coordinates of four corners in original page --- size and return coordinate of upper left conner in --- zoomed page size with width and height. ----------------------------------------------------- -function UniReader:zoomedRectCoordTransform(x0, y0, x1, y1) - local x,y = self:screenOffset() - return - x0 * self.globalzoom + x, - y0 * self.globalzoom + y, - (x1 - x0) * self.globalzoom, - (y1 - y0) * self.globalzoom -end - ----------------------------------------------------- --- Given coordinates of four corners in original page --- size and return rectangular area in screen. You --- might want to call this when you want to draw stuff --- on screen. --- --- NOTE: this method does not check whether given area --- is can be shown in current screen. Make sure to check --- with _isEntireWordInScreenRange() or _isWordInScreenRange() --- before you want to draw on the returned area. ----------------------------------------------------- -function UniReader:getRectInScreen(x0, y0, x1, y1) - x, y, w, h = self:zoomedRectCoordTransform(x0, y0, x1, y1) - if x < 0 then - w = w + x - x = 0 - end - if y < 0 then - h = h + y - y = 0 - end - if x + w > G_width then w = G_width - x end - if y + h > G_height then h = G_height - y end - return x, y, w, h -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) -function UniReader:_isBoxInNextView(box) - return box.y1 * self.globalzoom > -self.offset_y + G_height -end - -function UniReader:_isBoxInPrevView(box) - return box.y0 * self.globalzoom < -self.offset_y -end - --- make sure the whole word/line can be seen in screen --- @TODO when not in FIT_TO_CONTENT_WIDTH mode, --- self.offset_{x,y} might be negative. 12.04 2012 (houqp) -function UniReader:_isEntireLineInScreenHeightRange(l) - return (l ~= nil) and - (l.y0 * self.globalzoom) >= -self.offset_y - and (l.y1 * self.globalzoom) <= -self.offset_y + G_height -end - -function UniReader:_isEntireWordInScreenRange(w) - return self:_isEntireWordInScreenHeightRange(w) and - self:_isEntireWordInScreenWidthRange(w) -end - -function UniReader:_isEntireWordInScreenHeightRange(w) - return (w ~= nil) and - (w.y0 * self.globalzoom) >= -self.offset_y - and (w.y1 * self.globalzoom) <= -self.offset_y + G_height -end - -function UniReader:_isEntireWordInScreenWidthRange(w) - return (w ~= nil) and - (w.x0 * self.globalzoom >= -self.offset_x) and - (w.x1 * self.globalzoom <= -self.offset_x + G_width) -end - --- make sure at least part of the word can be seen in screen -function UniReader:_isWordInScreenRange(w) - if not w then - return false - end - - is_entire_word_out_of_screen_height = - (w.y1 * self.globalzoom <= -self.offset_y) - or (w.y0 * 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 - -function UniReader:_isWordInNextView(w) - return self:_isBoxInNextView(w) -end - -function UniReader:_isLineInNextView(l) - return self:_isBoxInNextView(l) -end - -function UniReader:_isLineInPrevView(l) - return self:_isBoxInPrevView(l) -end - -function UniReader:toggleTextHighLight(word_list) - for _,text_item in ipairs(word_list) do - for _,line_item in ipairs(text_item) do - -- make sure that line is in screen range - if self:_isWordInScreenRange(line_item) then - local x, y, w, h = self:getRectInScreen( - line_item.x0, line_item.y0, - line_item.x1, line_item.y1) - -- slightly enlarge the highlight height - -- for better viewing experience - x = x - y = y - h * 0.1 - w = w - h = h * 1.2 - - self.highlight.drawer = self.highlight.drawer or "underscore" - if self.highlight.drawer == "underscore" then - self.highlight.line_width = self.highlight.line_width or 2 - self.highlight.line_color = self.highlight.line_color or 5 - fb.bb:paintRect(x, y+h-1, w, - self.highlight.line_width, - self.highlight.line_color) - elseif self.highlight.drawer == "marker" then - fb.bb:invertRect(x, y, w, h) - end - end -- if isEntireWordInScreenHeightRange - end -- for line_item - end -- for text_item -end - -function UniReader:_wordIterFromRange(t, l0, w0, l1, w1) - local i = l0 - local j = w0 - 1 - return function() - if i <= l1 then - -- if in line range, loop through lines - if i == l1 then - -- in last line - if j < w1 then - j = j + 1 - else - -- out of range return nil - return nil, nil - end - else - if j < #t[i] then - j = j + 1 - else - -- goto next line - i = i + 1 - j = 1 - end - end - return i, j - end - end -- closure -end - -function UniReader:_toggleWordHighLight(t, l, w) - x, y, w, h = self:getRectInScreen(t[l][w].x0, t[l].y0, - t[l][w].x1, t[l].y1) - -- slightly enlarge the highlight range for better viewing experience - x = x - w * 0.05 - y = y - h * 0.05 - w = w * 1.1 - h = h * 1.1 - - fb.bb:invertRect(x, y, w, h) -end - -function UniReader:_toggleTextHighLight(t, l0, w0, l1, w1) - debug("_toggleTextHighLight range", l0, w0, l1, w1) - -- make sure (l0, w0) is smaller than (l1, w1) - if l0 > l1 then - l0, l1 = l1, l0 - w0, w1 = w1, w0 - elseif l0 == l1 and w0 > w1 then - w0, w1 = w1, w0 - end - - for _l, _w in self:_wordIterFromRange(t, l0, w0, l1, w1) do - if self:_isWordInScreenRange(t[_l][_w]) then - -- blitbuffer module will take care of the out of screen range part. - self:_toggleWordHighLight(t, _l, _w) - end - end -end - --- remember to clear cursor before calling this -function UniReader:drawCursorAfterWord(t, l, w) - -- get height of line t[l][w] is in - local _, _, _, h = self:zoomedRectCoordTransform(0, t[l].y0, 0, t[l].y1) - -- get rect of t[l][w] - local x, y, wd, _ = self:getRectInScreen(t[l][w].x0, t[l][w].y0, t[l][w].x1, t[l][w].y1) - self.cursor:setHeight(h) - self.cursor:moveTo(x+wd, y) - self.cursor:draw() -end - -function UniReader:drawCursorBeforeWord(t, l, w) - -- get height of line t[l][w] is in - local _, _, _, h = self:zoomedRectCoordTransform(0, t[l].y0, 0, t[l].y1) - -- get rect of t[l][w] - local x, y, _, _ = self:getRectInScreen(t[l][w].x0, t[l][w].y0, t[l][w].x1, t[l][w].y1) - self.cursor:setHeight(h) - self.cursor:moveTo(x, y) - self.cursor:draw() -end - -function UniReader:getText(pageno) - -- define a sensible implementation when your reader supports it - return nil -end - -function UniReader:startHighLightMode() - local t = self:getText(self.pageno) - if not t or #t == 0 then - showInfoMsgWithDelay("No text available for highlight", 2000, 1); - return nil - end - - local function _findFirstWordInView(t) - for i=1, #t, 1 do - if self:_isEntireWordInScreenRange(t[i][1]) then - return i, 1 - end - end - - showInfoMsgWithDelay("No visible text for highlight", 2000, 1); - debug("_findFirstWordInView none found in", t) - - return nil - end - - local function _isMovingForward(l, w) - return l.cur > l.start or (l.cur == l.start and w.cur > w.start) - end - - --------------------------------------- - -- some word handling help functions - --------------------------------------- - local function _prevWord(t, cur_l, cur_w) - if cur_l == 1 then - if cur_w == 1 then - -- already the first word - return 1, 1 - else - -- in first line, but not first word - return cur_l, cur_w -1 - end - end - - if cur_w <= 1 then - -- first word in current line, goto previous line - return cur_l - 1, #t[cur_l-1] - else - return cur_l, cur_w - 1 - end - end - - local function _nextWord(t, cur_l, cur_w) - if cur_l == #t then - if cur_w == #(t[cur_l]) then - -- already the last word - return cur_l, cur_w - else - -- in last line, but not last word - return cur_l, cur_w + 1 - end - end - - if cur_w < #t[cur_l] then - return cur_l, cur_w + 1 - else - -- last word in current line, move to next line - return cur_l + 1, 1 - end - end - - local function _wordInNextLine(t, cur_l, cur_w) - if cur_l == #t then - -- already in last line, return the last word - return cur_l, #(t[cur_l]) - else - return cur_l + 1, math.min(cur_w, #t[cur_l+1]) - end - end - - local function _wordInPrevLine(t, cur_l, cur_w) - if cur_l == 1 then - -- already in first line, return the first word - return 1, 1 - else - return cur_l - 1, math.min(cur_w, #t[cur_l-1]) - end - end - - --------------------------------------- - -- some gap handling help functions - --------------------------------------- - local function _nextGap(t, cur_l, cur_w) - local is_meet_end = false - - -- handle left end of line as special case. - if cur_w == 0 then - if cur_l == #t and #t[cur_l] == 1 then - is_meet_end = true - end - return cur_l, 1, is_meet_end - end - - cur_l, cur_w = _nextWord(t, cur_l, cur_w) - if cur_w == 1 then - cur_w = 0 - end - if cur_w ~= 0 and cur_l == #t and cur_w == #t[cur_l] then - is_meet_end = true - end - return cur_l, cur_w, is_meet_end - end - - local function _prevGap(t, cur_l, cur_w) - local is_meet_start = false - - -- handle left end of line as special case. - if cur_l == 1 and (cur_w == 1 or cur_w == 0) then -- in the first line - is_meet_start = true - return cur_l, 0, is_meet_start - end - if cur_w == 1 then -- not in the first line - return cur_l, 0, is_meet_start - elseif cur_w == 0 then - -- set to 1 so _prevWord() can find previous word in previous line - cur_w = 1 - end - - cur_l, cur_w = _prevWord(t, cur_l, cur_w) - return cur_l, cur_w, is_meet_end - end - - local function _gapInNextLine(t, cur_l, cur_w) - local is_meet_end = false - - if cur_l == #t then - -- already in last line - cur_w = #t[cur_l] - is_meet_end = true - else - -- handle left end of line as special case. - if cur_w == 0 then - cur_l = math.min(cur_l + 1, #t) - else - cur_l, cur_w = _wordInNextLine(t, cur_l, cur_w) - end - end - - return cur_l, cur_w, is_meet_end - end - - local function _gapInPrevLine(t, cur_l, cur_w) - local is_meet_start = false - - if cur_l == 1 then - -- already in first line - is_meet_start = true - cur_w = 0 - else - if cur_w == 0 then - -- goto left end of previous line - cur_l = math.max(cur_l - 1, 1) - else - cur_l, cur_w = _wordInPrevLine(t, cur_l, cur_w) - end - end - - return cur_l, cur_w, is_meet_start - end - - - local l = {} - local w = {} - - l.start, w.start = _findFirstWordInView(t) - if not l.start then - debug("no text in current view!") - -- InfoMessage about reason already shown - return - end - - w.start = 0 - l.cur, w.cur = l.start, w.start - l.new, w.new = l.cur, w.cur - local is_meet_start = false - local is_meet_end = false - local running = true - - local cx, cy, cw, ch = self:getRectInScreen( - t[l.cur][1].x0, - t[l.cur][1].y0, - t[l.cur][1].x1, - t[l.cur][1].y1) - - self.cursor = Cursor:new { - x_pos = cx, - y_pos = cy, - h = ch, - line_width_factor = 4, - } - self.cursor:draw() - fb:refresh(1) - - -- first use cursor to place start pos for highlight - while running do - local ev = input.saveWaitForEvent() - ev.code = adjustKeyEvents(ev) - if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then - if ev.code == KEY_FW_LEFT and not is_meet_start then - is_meet_end = false - l.new, w.new, is_meet_start = _prevGap(t, l.cur, w.cur) - - self.cursor:clear() - if w.new ~= 0 - and self:_isLineInPrevView(t[l.new]) - and self:_isEntireWordInScreenWidthRange(t[l.new][w.new]) then - -- word is in previous view - local pageno = self:prevView() - self:goto(pageno) - end - - -- update cursor - if w.new == 0 then - -- meet line left end, must be handled as special case - if self:_isEntireWordInScreenRange(t[l.new][1]) then - self:drawCursorBeforeWord(t, l.new, 1) - end - else - if self:_isEntireWordInScreenRange(t[l.new][w.new]) then - self:drawCursorAfterWord(t, l.new, w.new) - end - end - elseif ev.code == KEY_FW_RIGHT and not is_meet_end then - is_meet_start = false - l.new, w.new, is_meet_end = _nextGap(t, l.cur, w.cur) - - self.cursor:clear() - -- we want to check whether the word is in screen range, - -- so trun gap into word - local tmp_w = w.new - if tmp_w == 0 then - tmp_w = 1 - end - if self:_isLineInNextView(t[l.new]) - and self:_isEntireWordInScreenWidthRange(t[l.new][tmp_w]) then - local pageno = self:nextView() - self:goto(pageno) - end - - if w.new == 0 then - -- meet line left end, must be handled as special case - if self:_isEntireWordInScreenRange(t[l.new][1]) then - self:drawCursorBeforeWord(t, l.new, 1) - end - else - if self:_isEntireWordInScreenRange(t[l.new][w.new]) then - self:drawCursorAfterWord(t, l.new, w.new) - end - end - elseif ev.code == KEY_FW_UP and not is_meet_start then - is_meet_end = false - l.new, w.new, is_meet_start = _gapInPrevLine(t, l.cur, w.cur) - - self.cursor:clear() - - local tmp_w = w.new - if tmp_w == 0 then - tmp_w = 1 - end - if self:_isLineInPrevView(t[l.new]) - and self:_isEntireWordInScreenWidthRange(t[l.new][tmp_w]) then - -- goto next view of current page - local pageno = self:prevView() - self:goto(pageno) - end - - if w.new == 0 then - if self:_isEntireWordInScreenRange(t[l.new][1]) then - self:drawCursorBeforeWord(t, l.new, 1) - end - else - if self:_isEntireWordInScreenRange(t[l.new][w.new]) then - self:drawCursorAfterWord(t, l.new, w.new) - end - end - elseif ev.code == KEY_FW_DOWN and not is_meet_end then - is_meet_start = false - l.new, w.new, is_meet_end = _gapInNextLine(t, l.cur, w.cur) - - self.cursor:clear() - - local tmp_w = w.new - if w.cur == 0 then - tmp_w = 1 - end - if self:_isLineInNextView(t[l.new]) - and self:_isEntireWordInScreenWidthRange(t[l.new][tmp_w]) then - -- goto next view of current page - local pageno = self:nextView() - self:goto(pageno) - end - - if w.cur == 0 then - if self:_isEntireWordInScreenRange(t[l.new][1]) then - self:drawCursorBeforeWord(t, l.new, 1) - end - else - if self:_isEntireWordInScreenRange(t[l.new][w.new]) then - self:drawCursorAfterWord(t, l.new, w.new) - end - end - elseif ev.code == KEY_DEL then - -- handle left end of line as special case - if w.cur == 0 then - w.cur = 1 - end - if self.highlight[self.pageno] then - for k, text_item in ipairs(self.highlight[self.pageno]) do - for _, line_item in ipairs(text_item) do - if t[l.cur][w.cur].y0 >= line_item.y0 - and t[l.cur][w.cur].y1 <= line_item.y1 - and t[l.cur][w.cur].x0 >= line_item.x0 - and t[l.cur][w.cur].x1 <= line_item.x1 then - self.highlight[self.pageno][k] = nil - -- remove page entry if empty - if #self.highlight[self.pageno] == 0 then - self.highlight[self.pageno] = nil - end - return - end - end -- for line_item - end -- for text_item - end -- if not highlight table - elseif ev.code == KEY_FW_PRESS then - l.new, w.new = l.cur, w.cur - l.start, w.start = l.cur, w.cur - running = false - self.cursor:clear() - elseif ev.code == KEY_BACK then - running = false - return - end -- if check key event - l.cur, w.cur = l.new, w.new - fb:refresh(1) - end - end -- while running - debug("start", l.cur, w.cur, l.start, w.start) - - -- two helper functions for highlight - local function _togglePrevWordHighLight(t, l, w) - if w.cur == 0 then - if l.cur == 1 then - -- already at the begin of first line, nothing to toggle - return l, w, true - else - w.cur = 1 - end - end - l.new, w.new = _prevWord(t, l.cur, w.cur) - - if l.cur == 1 and w.cur == 1 then - is_meet_start = true - -- left end of first line must be handled as special case - w.new = 0 - end - - if w.new ~= 0 and - self:_isLineInPrevView(t[l.new]) then - -- word out of left and right sides of current view should - -- not trigger pan by page - if self:_isEntireWordInScreenWidthRange(t[l.new][w.new]) then - -- word is in previous view - local pageno = self:prevView() - self:goto(pageno) - end - - local l0 = l.start - local w0 = w.start - local l1 = l.cur - local w1 = w.cur - if _isMovingForward(l, w) then - l0, w0 = _nextWord(t, l0, w0) - l1, w1 = l.new, w.new - end - self:_toggleTextHighLight(t, l0, w0, - l1, w1) - else - self:_toggleWordHighLight(t, l.cur, w.cur) - end - - l.cur, w.cur = l.new, w.new - return l, w, (is_meet_start or false) - end - - local function _toggleNextWordHighLight(t, l, w) - if w.cur == 0 then - w.new = 1 - else - l.new, w.new = _nextWord(t, l.cur, w.cur) - end - if l.new == #t and w.new == #t[#t] then - is_meet_end = true - end - - if self:_isLineInNextView(t[l.new]) then - if self:_isEntireWordInScreenWidthRange(t[l.new][w.new]) then - local pageno = self:nextView() - self:goto(pageno) - end - - local tmp_l = l.start - local tmp_w = w.start - if _isMovingForward(l, w) then - tmp_l, tmp_w = _nextWord(t, tmp_l, tmp_w) - end - self:_toggleTextHighLight(t, tmp_l, tmp_w, - l.new, w.new) - else - self:_toggleWordHighLight(t, l.new, w.new) - end - - l.cur, w.cur = l.new, w.new - return l, w, (is_meet_end or false) - end - - - -- go into highlight mode - running = true - while running do - local ev = input.saveWaitForEvent() - ev.code = adjustKeyEvents(ev) - if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then - if ev.code == KEY_FW_LEFT then - is_meet_end = false - if not is_meet_start then - l, w, is_meet_start = _togglePrevWordHighLight(t, l, w) - end - elseif ev.code == KEY_FW_RIGHT then - is_meet_start = false - if not is_meet_end then - l, w, is_meet_end = _toggleNextWordHighLight(t, l, w) - end -- if not is_meet_end - elseif ev.code == KEY_FW_UP then - is_meet_end = false - if not is_meet_start then - if l.cur == 1 then - -- handle left end of first line as special case - tmp_l = 1 - tmp_w = 0 - else - tmp_l, tmp_w = _wordInPrevLine(t, l.cur, w.cur) - end - while not (tmp_l == l.cur and tmp_w == w.cur) do - l, w, is_meet_start = _togglePrevWordHighLight(t, l, w) - end - end -- not is_meet_start - elseif ev.code == KEY_FW_DOWN then - is_meet_start = false - if not is_meet_end then - -- handle left end of first line as special case - if w.cur == 0 then - tmp_w = 1 - else - tmp_w = w.cur - end - tmp_l, tmp_w = _wordInNextLine(t, l.cur, tmp_w) - while not (tmp_l == l.cur and tmp_w == w.cur) do - l, w, is_meet_end = _toggleNextWordHighLight(t, l, w) - end - end - elseif ev.code == KEY_FW_PRESS then - local l0, w0, l1, w1 - - -- find start and end of highlight text - if _isMovingForward(l, w) then - l0, w0 = _nextWord(t, l.start, w.start) - l1, w1 = l.cur, w.cur - else - l0, w0 = _nextWord(t, l.cur, w.cur) - l1, w1 = l.start, w.start - end - -- remove selection area - self:_toggleTextHighLight(t, l0, w0, l1, w1) - - -- put text into highlight table of current page - local hl_item = {} - local s = "" - local prev_l = l0 - local prev_w = w0 - local l_item = { - x0 = t[l0][w0].x0, - y0 = t[l0].y0, - y1 = t[l0].y1, - } - for _l,_w in self:_wordIterFromRange(t, l0, w0, l1, w1) do - local word_item = t[_l][_w] - if _l > prev_l then - -- in next line, add previous line to highlight item - l_item.x1 = t[prev_l][prev_w].x1 - table.insert(hl_item, l_item) - -- re initialize l_item for new line - l_item = { - x0 = word_item.x0, - y0 = t[_l].y0, - y1 = t[_l].y1, - } - end - s = s .. word_item.word .. " " - prev_l, prev_w = _l, _w - end - -- insert last line of text in line item - l_item.x1 = t[prev_l][prev_w].x1 - table.insert(hl_item, l_item) - hl_item.text = s - - if not self.highlight[self.pageno] then - self.highlight[self.pageno] = {} - end - table.insert(self.highlight[self.pageno], hl_item) - - running = false - elseif ev.code == KEY_BACK then - running = false - end -- if key event - fb:refresh(1) - end - end -- EOF while -end - - ----------------------------------------------------- --- Renderer memory ----------------------------------------------------- - -function UniReader:getCacheSize() - return -1 -end - -function UniReader:cleanCache() - return -end - ----------------------------------------------------- --- Setting related methods ----------------------------------------------------- - --- load special settings for specific reader -function UniReader:loadSpecialSettings() - return -end - --- save special settings for specific reader -function UniReader:saveSpecialSettings() -end - - - ---[ following are default methods ]-- - -function UniReader:initGlobalSettings(settings) - local pan_margin = settings:readSetting("pan_margin") - if pan_margin then - self.pan_margin = pan_margin - end - - local pan_overlap_vertical = settings:readSetting("pan_overlap_vertical") - if pan_overlap_vertical then - self.pan_overlap_vertical = pan_overlap_vertical - end - - local cache_max_memsize = settings:readSetting("cache_max_memsize") - if cache_max_memsize then - self.cache_max_memsize = cache_max_memsize - end - - local cache_max_ttl = settings:readSetting("cache_max_ttl") - if cache_max_ttl then - self.cache_max_ttl = cache_max_ttl - end - - local rcountmax = settings:readSetting("partial_refresh_count") - if rcountmax then - self.rcountmax = rcountmax - end -end - --- Method to load settings before document open -function UniReader:preLoadSettings(filename) - self.settings = DocSettings:open(filename) - - local cache_d_size = self.settings:readSetting("cache_document_size") - if cache_d_size then - self.cache_document_size = cache_d_size - end -end - --- This is a low-level method that can be shared with all readers. -function UniReader:loadSettings(filename) - if self.doc ~= nil then - local gamma = self.settings:readSetting("gamma") - if gamma then - self.globalgamma = gamma - end - - local jump_history = self.settings:readSetting("jump_history") - if jump_history then - self.jump_history = jump_history - else - self.jump_history = {cur = 1} - end - - local bookmarks = self.settings:readSetting("bookmarks") - self.bookmarks = bookmarks or {} - - -- clear obselate jumpstack settings - -- move jump_stack to bookmarks incase users used - -- it as bookmark feature before. - local jump_stack = self.settings:readSetting("jumpstack") - if jump_stack then - if #self.bookmarks == 0 then - self.bookmarks = jump_stack - end - self.settings:delSetting("jumpstack") - end - - local highlight = self.settings:readSetting("highlight") - self.highlight = highlight or {} - - local bbox = self.settings:readSetting("bbox") - debug("bbox loaded ", bbox) - self.bbox = bbox - - self.globalzoom = self.settings:readSetting("globalzoom") or 1.0 - self.globalzoom_mode = self.settings:readSetting("globalzoom_mode") or -1 - - self:loadSpecialSettings() - return true - end - return false -end - -function UniReader:getLastPageOrPos() - return self.settings:readSetting("last_page") or 1 -end - -function UniReader:saveLastPageOrPos() - self.settings:saveSetting("last_page", self.pageno) -end - --- guarantee that we have enough memory in cache -function UniReader:cacheClaim(size) - if(size > self.cache_max_memsize) then - -- we're not allowed to claim this much at all - error("too much memory claimed") - return false - end - while self.cache_current_memsize + size > self.cache_max_memsize do - -- repeat this until we have enough free memory - for k, _ in pairs(self.cache) do - if self.cache[k].ttl > 0 then - -- reduce ttl - self.cache[k].ttl = self.cache[k].ttl - 1 - else - -- cache slot is at end of life, so kick it out - self.cache_current_memsize = self.cache_current_memsize - self.cache[k].size - self.cache[k].bb:free() - self.cache[k] = nil - end - end - end - self.cache_current_memsize = self.cache_current_memsize + size - return true -end - -function UniReader:drawOrCache(no, preCache) - -- our general caching strategy is as follows: - -- #1 goal: we must render the needed area. - -- #2 goal: we render as much of the requested page as we can - -- #3 goal: we render the full page - -- #4 goal: we render next page, too. (TODO) - - -- ideally, this should be factored out and only be called when needed (TODO) - local ok, page = pcall(self.doc.openPage, self.doc, no) - local width, height = G_width, G_height - if not ok then - -- TODO: error handling - return nil - end - local dc = self:setzoom(page, preCache) - - -- offset_x_in_page & offset_y_in_page is the offset within zoomed page - -- they are always positive. - -- you can see self.offset_x_& self.offset_y as the offset within - -- draw space, which includes the page. So it can be negative and positive. - local offset_x_in_page = -self.offset_x - local offset_y_in_page = -self.offset_y - if offset_x_in_page < 0 then offset_x_in_page = 0 end - if offset_y_in_page < 0 then offset_y_in_page = 0 end - - -- check if we have relevant cache contents - local pagehash = no..'_'..self.globalzoom..'_'..self.globalrotate..'_'..self.globalgamma - if self.cache[pagehash] ~= nil then - -- we have something in cache, check if it contains the requested part - if self.cache[pagehash].x <= offset_x_in_page - and self.cache[pagehash].y <= offset_y_in_page - and ( self.cache[pagehash].x + self.cache[pagehash].w >= offset_x_in_page + width - or self.cache[pagehash].w >= self.fullwidth - 1) - and ( self.cache[pagehash].y + self.cache[pagehash].h >= offset_y_in_page + height - or self.cache[pagehash].h >= self.fullheight - 1) - then - -- requested part is within cached tile - -- ...so properly clean page - page:close() - -- ...and give it more time to live (ttl), except if we're precaching - if not preCache then - self.cache[pagehash].ttl = self.cache_max_ttl - end - -- ...and return blitbuffer plus offset into it - return pagehash, - offset_x_in_page - self.cache[pagehash].x, - offset_y_in_page - self.cache[pagehash].y - end - end - -- okay, we do not have it in cache yet. - -- so render now. - -- start off with the requested area - local tile = { x = offset_x_in_page, y = offset_y_in_page, - w = width, h = height } - -- can we cache the full page? - local max_cache = self.cache_max_memsize - if preCache then - max_cache = max_cache - self.cache[self.pagehash].size - end - if (self.fullwidth * self.fullheight / 2) <= max_cache then - -- yes we can, so do this with offset 0, 0 - tile.x = 0 - tile.y = 0 - tile.w = self.fullwidth - tile.h = self.fullheight - elseif (tile.w*tile.h / 2) < max_cache then - -- no, we can't. so generate a tile as big as we can go - -- grow area in steps of 10px - while ((tile.w+10) * (tile.h+10) / 2) < max_cache do - if tile.x > 0 then - tile.x = tile.x - 5 - tile.w = tile.w + 5 - end - if tile.x + tile.w < self.fullwidth then - tile.w = tile.w + 5 - end - if tile.y > 0 then - tile.y = tile.y - 5 - tile.h = tile.h + 5 - end - if tile.y + tile.h < self.fullheight then - tile.h = tile.h + 5 - end - end - else - if not preCache then - debug("ERROR not enough memory in cache left, probably a bug.") - end - return nil - end - self:cacheClaim(tile.w * tile.h / 2); - self.cache[pagehash] = { - x = tile.x, - y = tile.y, - w = tile.w, - h = tile.h, - ttl = self.cache_max_ttl, - size = tile.w * tile.h / 2, - bb = Blitbuffer.new(tile.w, tile.h) - } - --debug ("# new biltbuffer:"..dump(self.cache[pagehash])) - dc:setOffset(-tile.x, -tile.y) - debug("rendering page", no) - page:draw(dc, self.cache[pagehash].bb, 0, 0) - page:close() - - -- return hash and offset within blitbuffer - return pagehash, - offset_x_in_page - tile.x, - offset_y_in_page - tile.y -end - --- blank the cache -function UniReader:clearCache() - for k, _ in pairs(self.cache) do - self.cache[k].bb:free() - end - self.cache = {} - self.cache_current_memsize = 0 -end - --- set viewer state according to zoom state -function UniReader:setzoom(page, preCache) - local dc = DrawContext.new() - local pwidth, pheight = page:getSize(self.nulldc) - local width, height = G_width, G_height - debug("page::getSize",pwidth,pheight) - local x0, y0, x1, y1 = page:getUsedBBox() - if x0 == 0.01 and y0 == 0.01 and x1 == -0.01 and y1 == -0.01 then - x0 = 0 - y0 = 0 - x1 = pwidth - y1 = pheight - end - if x1 == 0 then x1 = pwidth end - if y1 == 0 then y1 = pheight end - -- clamp to page BBox - if x0 < 0 then x0 = 0 end - if x1 > pwidth then x1 = pwidth end - if y0 < 0 then y0 = 0 end - if y1 > pheight then y1 = pheight end - - if self.bbox.enabled then - debug("ORIGINAL page::getUsedBBox", x0,y0, x1,y1 ) - local bbox = self.bbox[self.pageno] -- exact - - local oddEven = self:oddEven(self.pageno) - if bbox ~= nil then - debug("bbox from", self.pageno) - else - bbox = self.bbox[oddEven] -- odd/even - end - if bbox ~= nil then -- last used up to this page - debug("bbox from", oddEven) - else - for i = 0,self.pageno do - bbox = self.bbox[ self.pageno - i ] - if bbox ~= nil then - debug("bbox from", self.pageno - i) - break - end - end - end - if bbox ~= nil then - x0 = bbox["x0"] - y0 = bbox["y0"] - x1 = bbox["x1"] - y1 = bbox["y1"] - end - end - - debug("page::getUsedBBox", x0, y0, x1, y1 ) - - if self.globalzoom_mode == self.ZOOM_FIT_TO_PAGE - or self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT then - self.globalzoom = width / pwidth - self.offset_x = 0 - self.offset_y = (height - (self.globalzoom * pheight)) / 2 - if height / pheight < self.globalzoom then - self.globalzoom = height / pheight - self.offset_x = (width - (self.globalzoom * pwidth)) / 2 - self.offset_y = 0 - end - self.pan_by_page = false - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_PAGE_WIDTH - or self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH then - self.globalzoom = width / pwidth - self.offset_x = 0 - self.offset_y = (height - (self.globalzoom * pheight)) / 2 - self.pan_by_page = false - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_PAGE_HEIGHT - or self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then - self.globalzoom = height / pheight - self.offset_x = (width - (self.globalzoom * pwidth)) / 2 - self.offset_y = 0 - self.pan_by_page = false - end - - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT then - if (x1 - x0) < pwidth then - self.globalzoom = width / (x1 - x0) - if height / (y1 - y0) < self.globalzoom then - self.globalzoom = height / (y1 - y0) - end - end - self.offset_x = -1 * x0 * self.globalzoom - self.offset_y = -1 * y0 * self.globalzoom - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH then - if (x1 - x0) < pwidth then - self.globalzoom = width / (x1 - x0) - end - self.offset_x = -1 * x0 * self.globalzoom - self.offset_y = -1 * y0 * self.globalzoom - self.content_top = self.offset_y - -- enable pan mode in ZOOM_FIT_TO_CONTENT_WIDTH - self.globalzoom_mode = self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - if self.content_top == -2012 then - -- We must handle previous page turn as a special cases, - -- because we want to arrive at the bottom of previous page. - -- Since this a real page turn, we need to recalculate stuff. - if (x1 - x0) < pwidth then - self.globalzoom = width / (x1 - x0) - end - self.offset_x = -1 * x0 * self.globalzoom - self.content_top = -1 * y0 * self.globalzoom - self.offset_y = fb.bb:getHeight() - self.fullheight - end - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then - if (y1 - y0) < pheight then - self.globalzoom = height / (y1 - y0) - end - self.offset_x = -1 * x0 * self.globalzoom - self.offset_y = -1 * y0 * self.globalzoom - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH - or self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN then - local margin = self.pan_margin - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH then margin = 0 end - self.globalzoom = width / (x1 - x0 + margin) - self.offset_x = -1 * x0 * self.globalzoom * 2 + margin - self.globalzoom = height / (y1 - y0 + margin) - self.offset_y = -1 * y0 * self.globalzoom * 2 + margin - self.globalzoom = width / (x1 - x0 + margin) * 2 - debug("column mode offset:", self.offset_x, self.offset_y, " zoom:", self.globalzoom); - self.globalzoom_mode = self.ZOOM_BY_VALUE -- enable pan mode - self.pan_x = self.offset_x - self.pan_y = self.offset_y - self.pan_by_page = true - end - - dc:setZoom(self.globalzoom) - self.globalzoom_orig = self.globalzoom - - dc:setRotate(self.globalrotate); - self.fullwidth, self.fullheight = page:getSize(dc) - if not preCache then -- save current page fullsize - self.cur_full_width = self.fullwidth - self.cur_full_height = self.fullheight - - self.cur_bbox = { - ["x0"] = x0, - ["y0"] = y0, - ["x1"] = x1, - ["y1"] = y1, - } - debug("cur_bbox", self.cur_bbox) - - end - self.min_offset_x = fb.bb:getWidth() - self.fullwidth - self.min_offset_y = fb.bb:getHeight() - self.fullheight - if(self.min_offset_x > 0) then - self.min_offset_x = 0 - end - if(self.min_offset_y > 0) then - self.min_offset_y = 0 - end - - debug("Reader:setZoom globalzoom:", self.globalzoom, " globalrotate:", self.globalrotate, " offset:", self.offset_x, self.offset_y, " pagesize:", self.fullwidth, self.fullheight, " min_offset:", self.min_offset_x, self.min_offset_y) - - -- set gamma here, we don't have any other good place for this right now: - if self.globalgamma ~= self.GAMMA_NO_GAMMA then - debug("gamma correction: ", self.globalgamma) - dc:setGamma(self.globalgamma) - end - return dc -end - --- render and blit a page -function UniReader:show(no) - local pagehash, offset_x, offset_y = self:drawOrCache(no) - local width, height = G_width, G_height - - if not pagehash then - return - end - self.pagehash = pagehash - local bb = self.cache[pagehash].bb - self.dest_x = 0 - self.dest_y = 0 - if bb:getWidth() - offset_x < width then - -- we can't fill the whole output width, center the content - self.dest_x = (width - (bb:getWidth() - offset_x)) / 2 - end - if bb:getHeight() - offset_y < height and - self.globalzoom_mode ~= self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - -- we can't fill the whole output height and not in - -- ZOOM_FIT_TO_CONTENT_WIDTH_PAN mode, center the content - self.dest_y = (height - (bb:getHeight() - offset_y)) / 2 - elseif self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN and - self.offset_y > 0 then - -- if we are in ZOOM_FIT_TO_CONTENT_WIDTH_PAN mode and turning to - -- the top of the page, we might leave an empty space between the - -- page top and screen top. - self.dest_y = self.offset_y - end - if self.dest_x or self.dest_y then - fb.bb:paintRect(0, 0, width, height, 8) - end - debug("blitFrom dest_off:", self.dest_x, self.dest_y, - "src_off:", offset_x, offset_y, - "width:", width, "height:", height) - fb.bb:blitFrom(bb, self.dest_x, self.dest_y, offset_x, offset_y, width, height) - - debug("self.show_overlap", self.show_overlap) - if self.show_overlap < 0 then - fb.bb:dimRect(0,0, width, self.dest_y - self.show_overlap) - elseif self.show_overlap > 0 then - fb.bb:dimRect(0,self.dest_y + height - self.show_overlap, width, self.show_overlap) - end - self.show_overlap = 0 - - -- render highlights to page - if self.highlight[no] then - self:toggleTextHighLight(self.highlight[no]) - end - - 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.slot_visible = slot; -end - -function UniReader:isSamePage(p1, p2) - return p1 == p2 -end - ---[[ - @ pageno is the page you want to add to jump_history, this will - clear the forward stack since pageno is the new head. - NOTE: for CREReader, pageno refers to xpointer ---]] -function UniReader:addJump(pageno) - -- build notes from TOC - local notes = self:getTocTitleByPage(pageno) - if notes ~= "" then - notes = "in "..notes - end - -- create a head - jump_item = { - page = pageno, - datetime = os.date("%Y-%m-%d %H:%M:%S"), - notes = notes, - } - -- clear forward stack if it is not empty - if self.jump_history.cur < #self.jump_history then - for i=self.jump_history.cur+1, #self.jump_history do - self.jump_history[i] = nil - end - end - -- keep the size less than 10 - if #self.jump_history > 10 then - table.remove(self.jump_history) - end - -- set up new head - -- if backward stack top is the same as page to record, remove it - if #self.jump_history ~= 0 and - self:isSamePage(self.jump_history[#self.jump_history].page, pageno) then - self.jump_history[#self.jump_history] = nil - end - table.insert(self.jump_history, jump_item) - self.jump_history.cur = #self.jump_history + 1 - return true -end - -function UniReader:delJump(pageno) - for _t,_v in ipairs(self.jump_history) do - if _v.page == pageno then - table.remove(self.jump_history, _t) - end - end -end - --- return nil if page already marked --- otherwise, return true -function UniReader:addBookmark(pageno) - for k,v in ipairs(self.bookmarks) do - if v.page == pageno then - return nil - end - end - -- build notes from TOC - local notes = self:getTocTitleByPage(pageno) - if notes ~= "" then - notes = "in "..notes - end - mark_item = { - page = pageno, - datetime = os.date("%Y-%m-%d %H:%M:%S"), - notes = notes, - } - table.insert(self.bookmarks, mark_item) - return true -end - --- change current page and cache next page after rendering -function UniReader:goto(no, is_ignore_jump) - if no < 1 or no > self.doc:getPages() then - return - end - - -- for jump_history - if not is_ignore_jump then - -- distinguish jump from normal page turn - if self.pageno and math.abs(self.pageno - no) > 1 then - self:addJump(self.pageno) - end - end - - self.pageno = no - self:show(no) - - -- TODO: move the following to a more appropriate place - -- into the caching section - if no < self.doc:getPages() then - if #self.bbox == 0 or not self.bbox.enabled then - -- pre-cache next page, but if we will modify bbox don't! - self:drawOrCache(no+1, true) - end - end -end - -function UniReader:redrawCurrentPage() - self:goto(self.pageno) -end - -function UniReader:nextView() - local pageno = self.pageno - - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - if self.offset_y <= self.min_offset_y then - -- hit content bottom, turn to next page - self.globalzoom_mode = self.ZOOM_FIT_TO_CONTENT_WIDTH - pageno = pageno + 1 - else - -- goto next view of current page - self.offset_y = self.offset_y - G_height - + self.pan_overlap_vertical - self.show_overlap = -self.pan_overlap_vertical -- top < 0 - end - else - -- not in fit to content width pan mode, just do a page turn - pageno = pageno + 1 - if self.pan_by_page then - -- we are in two column mode - self.offset_x = self.pan_x - self.offset_y = self.pan_y - end - end - - return pageno -end - -function UniReader:prevView() - local pageno = self.pageno - - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - if self.offset_y >= self.content_top then - -- hit content top, turn to previous page - -- set self.content_top with magic num to signal self:setZoom - self.content_top = -2012 - pageno = pageno - 1 - else - -- goto previous view of current page - self.offset_y = self.offset_y + G_height - - self.pan_overlap_vertical - self.show_overlap = self.pan_overlap_vertical -- bottom > 0 - end - else - -- not in fit to content width pan mode, just do a page turn - pageno = pageno - 1 - if self.pan_by_page then - -- we are in two column mode - self.offset_x = self.pan_x - self.offset_y = self.pan_y - end - end - - return pageno -end - --- adjust global gamma setting -function UniReader:modifyGamma(factor) - debug("modifyGamma, gamma=", self.globalgamma, " factor=", factor) - self.globalgamma = self.globalgamma * factor; - self:redrawCurrentPage() -end - --- adjust zoom state and trigger re-rendering -function UniReader:setglobalzoom_mode(newzoommode) - if self.globalzoom_mode ~= newzoommode then - self.globalzoom_mode = newzoommode - self:redrawCurrentPage() - end -end - --- adjust zoom state and trigger re-rendering -function UniReader:setGlobalZoom(zoom) - if self.globalzoom ~= zoom then - self.globalzoom_mode = self.ZOOM_BY_VALUE - self.globalzoom = zoom - self:redrawCurrentPage() - end -end - -function UniReader:setRotate(rotate) - self.globalrotate = rotate - self:redrawCurrentPage() -end - --- @ orien: 1 for clockwise rotate, -1 for anti-clockwise -function UniReader:screenRotate(orien) - Screen:screenRotate(orien) - -- update global width and height variable - G_width, G_height = fb:getSize() - self:clearCache() -end - -function UniReader:cleanUpTocTitle(title) - return title:gsub("\13", "") -end - -function UniReader:fillToc() - self.toc = self.doc:getToc() -end - --- getTocTitleByPage wrapper, so specific reader --- can tranform pageno according its need -function UniReader:getTocTitleByPage(pageno) - return self:_getTocTitleByPage(pageno) -end - -function UniReader:_getTocTitleByPage(pageno) - if not self.toc then - -- build toc when needed. - self:fillToc() - end - - -- no table of content - if #self.toc == 0 then - return "" - end - - local pre_entry = self.toc[1] - for _k,_v in ipairs(self.toc) do - if _v.page > pageno then - break - end - pre_entry = _v - end - return self:cleanUpTocTitle(pre_entry.title) -end - -function UniReader:getTocTitleOfCurrentPage() - return self:getTocTitleByPage(self.pageno) -end - -function UniReader:gotoTocEntry(entry) - self:goto(entry.page) -end - -function UniReader:showToc() - if not self.toc then - -- build toc if needed. - self:fillToc() - end - - -- build menu items - local menu_items = {} - for k,v in ipairs(self.toc) do - table.insert(menu_items, - (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title)) - end - - if #menu_items == 0 then - showInfoMsgWithDelay( - "This document does not have a TOC.", 2000, 1) - else - toc_menu = SelectMenu:new{ - menu_title = "Table of Contents", - item_array = menu_items, - } - item_no = toc_menu:choose(0, fb.bb:getHeight()) - - if item_no then - self:gotoTocEntry(self.toc[item_no]) - else - self:redrawCurrentPage() - end - end -end - -function UniReader: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 "..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) - else - self:redrawCurrentPage() - end - end -end - -function UniReader:showBookMarks() - local menu_items = {} - -- build menu items - for k,v in ipairs(self.bookmarks) do - table.insert(menu_items, - "Page "..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) - else - self:redrawCurrentPage() - end - end -end - -function UniReader:showHighLight() - local menu_items = {} - local highlight_dict = {} - -- build menu items - for k,v in pairs(self.highlight) do - if type(k) == "number" then - for k1,v1 in ipairs(v) do - table.insert(menu_items, v1.text) - table.insert(highlight_dict, {page=k, start=v1[1]}) - end - end - end - if #menu_items == 0 then - showInfoMsgWithDelay( - "No HighLights found.", 2000, 1) - else - toc_menu = SelectMenu:new{ - menu_title = "HighLights", - item_array = menu_items, - } - item_no = toc_menu:choose(0, fb.bb:getHeight()) - if item_no then - self:goto(highlight_dict[item_no].page) - else - self:redrawCurrentPage() - end - end -end - --- used in UniReader:showMenu() -function UniReader:_drawReadingInfo() - local width, height = G_width, G_height - local load_percent = (self.pageno / self.doc:getPages()) - local face = Font:getFace("cfont", 22) - - -- display memory on top of page - fb.bb:paintRect(0, 0, width, 15+6*2, 0) - renderUtf8Text(fb.bb, 10, 15+6, face, - "Memory: ".. - math.ceil( self.cache_current_memsize / 1024 ).."/"..math.ceil( self.cache_max_memsize / 1024 ).. - " "..math.ceil( self.doc:getCacheSize() / 1024 ).."/"..math.ceil( self.cache_document_size / 1024 ).." k", - true) - - -- display reading progress on bottom of page - local ypos = height - 50 - fb.bb:paintRect(0, ypos, width, 50, 0) - ypos = ypos + 15 - local cur_section = self:getTocTitleOfCurrentPage() - if cur_section ~= "" then - cur_section = "Section: "..cur_section - end - renderUtf8Text(fb.bb, 10, ypos+6, face, - "Page: "..self.pageno.."/"..self.doc:getPages().. - " "..cur_section, true) - - ypos = ypos + 15 - blitbuffer.progressBar(fb.bb, 10, ypos, width-20, 15, - 5, 4, load_percent, 8) -end - -function UniReader:showMenu() - self:_drawReadingInfo() - - fb:refresh(1) - while 1 do - local ev = input.saveWaitForEvent() - ev.code = adjustKeyEvents(ev) - if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then - if ev.code == KEY_BACK or ev.code == KEY_MENU then - return - elseif ev.code == KEY_C then - self.doc:cleanCache() - end - end - end -end - -function UniReader:oddEven(number) - debug("oddEven", number) - if number % 2 == 1 then - return "odd" - else - return "even" - end -end - --- wait for input and handle it -function UniReader:inputLoop() - local keep_running = true - while 1 do - local ev = input.saveWaitForEvent() - ev.code = adjustKeyEvents(ev) - if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then - local secs, usecs = util.gettime() - 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) - if ret_code == "break" then - break; - end - else - debug("command not found:", tostring(command)) - end - - local nsecs, nusecs = util.gettime() - local dur = (nsecs - secs) * 1000000 + nusecs - usecs - debug("E: T="..ev.type, " V="..ev.value, " C="..ev.code, " DUR=", dur) - - if ev.value == EVENT_VALUE_KEY_REPEAT then - self.rcount = 0 - debug("prevent full screen refresh", self.rcount) - end - else - debug("ignored ev ",ev) - end - end - - -- do clean up stuff - self:clearCache() - self.toc = nil - if self.doc ~= nil then - self.doc:close() - end - if self.settings ~= nil then - self:saveLastPageOrPos() - self.settings:saveSetting("gamma", self.globalgamma) - self.settings:saveSetting("jump_history", self.jump_history) - self.settings:saveSetting("bookmarks", self.bookmarks) - self.settings:saveSetting("bbox", self.bbox) - self.settings:saveSetting("globalzoom", self.globalzoom) - self.settings:saveSetting("globalzoom_mode", self.globalzoom_mode) - self.settings:saveSetting("highlight", self.highlight) - self:saveSpecialSettings() - self.settings:close() - end - - return keep_running -end - --- command definitions -function UniReader:addAllCommands() - self.commands = Commands:new() - self.commands:addGroup("< >",{ - Keydef:new(KEY_PGBCK,nil),Keydef:new(KEY_LPGBCK,nil), - Keydef:new(KEY_PGFWD,nil),Keydef:new(KEY_LPGFWD,nil)}, - "previous/next page", - function(unireader,keydef) - unireader:goto( - (keydef.keycode == KEY_PGBCK or keydef.keycode == KEY_LPGBCK) - and unireader:prevView() or unireader:nextView()) - 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)}, - "zoom out/in 10%", - function(unireader,keydef) - is_zoom_out = (keydef.keycode == KEY_PGBCK or keydef.keycode == KEY_LPGBCK) - unireader:setGlobalZoom(unireader.globalzoom_orig - + (is_zoom_out and -1 or 1)*unireader.globalzoom_orig*0.1) - end) - 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)}, - "zoom out/in 20%", - function(unireader,keydef) - is_zoom_out = (keydef.keycode == KEY_PGBCK or keydef.keycode == KEY_LPGBCK) - unireader:setGlobalZoom(unireader.globalzoom_orig - + ( is_zoom_out and -1 or 1)*unireader.globalzoom_orig*0.2) - end) - self.commands:add(KEY_BACK,nil,"Back", - "go backward in jump history", - function(unireader) - local prev_jump_no = unireader.jump_history.cur - 1 - if prev_jump_no >= 1 then - unireader.jump_history.cur = prev_jump_no - unireader:goto(unireader.jump_history[prev_jump_no].page, true) - else - showInfoMsgWithDelay("Already first jump!", 2000, 1) - end - end) - self.commands:add(KEY_BACK,MOD_SHIFT,"Back", - "go forward in jump history", - function(unireader) - local next_jump_no = unireader.jump_history.cur + 1 - if next_jump_no <= #self.jump_history then - unireader.jump_history.cur = next_jump_no - unireader:goto(unireader.jump_history[next_jump_no].page, true) - else - showInfoMsgWithDelay("Already last jump!", 2000, 1) - end - end) - self.commands:add(KEY_BACK,MOD_ALT,"Back", - "close document", - function(unireader) - return "break" - end) - self.commands:add(KEY_HOME,nil,"Home", - "exit application", - function(unireader) - keep_running = false - return "break" - end) - self.commands:addGroup("vol-/+",{Keydef:new(KEY_VPLUS,nil),Keydef:new(KEY_VMINUS,nil)}, - "decrease/increase gamma 25%", - function(unireader,keydef) - unireader:modifyGamma(keydef.keycode==KEY_VPLUS and 1.25 or 0.8) - end) - --numeric key group - 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, 2 .. 9, 0]",numeric_keydefs, - "jump to 10%, 20% .. 90%, 100% of document", - function(unireader,keydef) - debug('jump to page:', math.max(math.floor(unireader.doc:getPages()*(keydef.keycode-KEY_1)/9),1), '/', unireader.doc:getPages()) - unireader:goto(math.max(math.floor(unireader.doc:getPages()*(keydef.keycode-KEY_1)/9),1)) - end) - -- end numeric keys - self.commands:add(KEY_A,nil,"A", - "zoom to fit page", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_PAGE) - end) - self.commands:add(KEY_A,MOD_SHIFT,"A", - "zoom to fit content", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_CONTENT) - end) - self.commands:add(KEY_S,nil,"S", - "zoom to fit page width", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_PAGE_WIDTH) - end) - self.commands:add(KEY_S,MOD_SHIFT,"S", - "zoom to fit content width", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_CONTENT_WIDTH) - end) - self.commands:add(KEY_D,nil,"D", - "zoom to fit page height", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_PAGE_HEIGHT) - end) - self.commands:add(KEY_D,MOD_SHIFT,"D", - "zoom to fit content height", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_CONTENT_HEIGHT) - end) - self.commands:add(KEY_F,nil,"F", - "zoom to fit margin 2-column mode", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN) - end) - self.commands:add(KEY_F,MOD_SHIFT,"F", - "zoom to fit content 2-column mode", - function(unireader) - unireader:setglobalzoom_mode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH) - end) - self.commands:add(KEY_G,nil,"G", - "open 'go to page' input box", - function(unireader) - local page = NumInputBox:input(G_height-100, 100, - "Page:", "current page "..self.pageno, true) - -- convert string to number - if not pcall(function () page = page + 0 end) then - page = unireader.pageno - else - if page < 1 or page > unireader.doc:getPages() then - page = unireader.pageno - end - end - unireader:goto(page) - end) - self.commands:add(KEY_H,nil,"H", - "show help page", - function(unireader) - HelpPage:show(0, G_height, unireader.commands) - unireader:redrawCurrentPage() - end) - self.commands:add(KEY_T,nil,"T", - "show table of content", - function(unireader) - unireader:showToc() - end) - self.commands:add(KEY_B,nil,"B", - "show bookmarks", - function(unireader) - unireader:showBookMarks() - end) - self.commands:add(KEY_B,MOD_ALT,"B", - "add bookmark to current page", - function(unireader) - ok = unireader:addBookmark(self.pageno) - if not ok then - showInfoMsgWithDelay("Page already marked!", 2000, 1) - else - showInfoMsgWithDelay("Page marked.", 2000, 1) - end - end) - self.commands:add(KEY_B,MOD_SHIFT,"B", - "show jump history", - function(unireader) - unireader:showJumpHist() - end) - self.commands:add(KEY_J,MOD_SHIFT,"J", - "rotate 10° clockwise", - function(unireader) - unireader:setRotate( unireader.globalrotate + 10 ) - end) - self.commands:add(KEY_J,nil,"J", - "rotate screen 90° clockwise", - function(unireader) - unireader:screenRotate("clockwise") - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - self:setglobalzoom_mode(self.ZOOM_FIT_TO_CONTENT_WIDTH) - else - self:redrawCurrentPage() - end - end) - self.commands:add(KEY_K,MOD_SHIFT,"K", - "rotate 10° counterclockwise", - function(unireader) - unireader:setRotate( unireader.globalrotate - 10 ) - end) - self.commands:add(KEY_K,nil,"K", - "rotate screen 90° counterclockwise", - function(unireader) - unireader:screenRotate("anticlockwise") - if self.globalzoom_mode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then - self:setglobalzoom_mode(self.ZOOM_FIT_TO_CONTENT_WIDTH) - else - self:redrawCurrentPage() - end - end) - self.commands:add(KEY_R, MOD_SHIFT, "R", - "manual full screen refresh", - function(unireader) - -- eink will not refresh if nothing is changeed on the screen - -- so we fake a change here. - fb.bb:invertRect(0, 0, 1, 1) - fb:refresh(1) - fb.bb:invertRect(0, 0, 1, 1) - fb:refresh(0) - unireader.rcount = 0 - end) - self.commands:add(KEY_Z,nil,"Z", - "set crop mode", - function(unireader) - local bbox = {} - bbox["x0"] = - unireader.offset_x / unireader.globalzoom - bbox["y0"] = - unireader.offset_y / unireader.globalzoom - bbox["x1"] = bbox["x0"] + G_width / unireader.globalzoom - bbox["y1"] = bbox["y0"] + G_height / unireader.globalzoom - bbox.pan_x = unireader.pan_x - bbox.pan_y = unireader.pan_y - unireader.bbox[unireader.pageno] = bbox - unireader.bbox[unireader:oddEven(unireader.pageno)] = bbox - unireader.bbox.enabled = true - debug("bbox", unireader.pageno, unireader.bbox) - unireader.globalzoom_mode = unireader.ZOOM_FIT_TO_CONTENT -- use bbox - showInfoMsgWithDelay("Manual crop setting saved.", 2000, 1) - end) - self.commands:add(KEY_Z,MOD_SHIFT,"Z", - "reset crop", - function(unireader) - unireader.bbox[unireader.pageno] = nil; - showInfoMsgWithDelay("Manual crop setting removed.", 2000, 1) - debug("bbox remove", unireader.pageno, unireader.bbox); - end) - self.commands:add(KEY_Z,MOD_ALT,"Z", - "toggle crop mode", - function(unireader) - unireader.bbox.enabled = not unireader.bbox.enabled; - if unireader.bbox.enabled then - showInfoMsgWithDelay("Manual crop enabled.", 2000, 1) - else - showInfoMsgWithDelay("Manual crop disabled.", 2000, 1) - end - debug("bbox override", unireader.bbox.enabled); - end) - self.commands:add(KEY_X,nil,"X", - "invert page bbox", - function(unireader) - local bbox = unireader.cur_bbox - debug("bbox", bbox) - x,y,w,h = unireader:getRectInScreen( bbox["x0"], bbox["y0"], bbox["x1"], bbox["y1"] ) - debug("inxertRect",x,y,w,h) - fb.bb:invertRect( x,y, w,h ) - fb:refresh(0) - end) - self.commands:add(KEY_MENU,nil,"Menu", - "toggle info box", - function(unireader) - unireader:showMenu() - unireader:redrawCurrentPage() - end) - -- panning - local panning_keys = {Keydef:new(KEY_FW_LEFT,MOD_ANY),Keydef:new(KEY_FW_RIGHT,MOD_ANY),Keydef:new(KEY_FW_UP,MOD_ANY),Keydef:new(KEY_FW_DOWN,MOD_ANY),Keydef:new(KEY_FW_PRESS,MOD_ANY)} - self.commands:addGroup("[joypad]",panning_keys, - "pan the active view; use Shift or Alt for smaller steps", - function(unireader,keydef) - if keydef.keycode ~= KEY_FW_PRESS then - unireader.globalzoom_mode = unireader.ZOOM_BY_VALUE - end - if unireader.globalzoom_mode == unireader.ZOOM_BY_VALUE then - local x - local y - if keydef.modifier==MOD_SHIFT then -- shift always moves in small steps - x = unireader.shift_x / 2 - y = unireader.shift_y / 2 - elseif keydef.modifier==MOD_ALT then - x = unireader.shift_x / 5 - y = unireader.shift_y / 5 - elseif unireader.pan_by_page then - x = G_width - y = G_height - unireader.pan_overlap_vertical -- overlap for lines which didn't fit - else - x = unireader.shift_x - y = unireader.shift_y - end - - debug("offset", unireader.offset_x, unireader.offset_x, " shift", x, y, " globalzoom", unireader.globalzoom) - local old_offset_x = unireader.offset_x - local old_offset_y = unireader.offset_y - - if keydef.keycode == KEY_FW_LEFT then - debug("KEY_FW_LEFT", unireader.offset_x, "+", x, "> 0"); - unireader.offset_x = unireader.offset_x + x - if unireader.pan_by_page then - if unireader.offset_x > 0 and unireader.pageno > 1 then - unireader.offset_x = unireader.pan_x - unireader.offset_y = unireader.min_offset_y -- bottom - unireader:goto(unireader.pageno - 1) - else - unireader.show_overlap = 0 - unireader.offset_y = unireader.min_offset_y - end - elseif unireader.offset_x > 0 then - unireader.offset_x = 0 - end - elseif keydef.keycode == KEY_FW_RIGHT then - debug("KEY_FW_RIGHT", unireader.offset_x, "-", x, "<", unireader.min_offset_x, "-", unireader.pan_margin); - unireader.offset_x = unireader.offset_x - x - if unireader.pan_by_page then - if unireader.offset_x < unireader.min_offset_x - unireader.pan_margin and unireader.pageno < unireader.doc:getPages() then - unireader.offset_x = unireader.pan_x - unireader.offset_y = unireader.pan_y - unireader:goto(unireader.pageno + 1) - else - unireader.show_overlap = 0 - unireader.offset_y = unireader.pan_y - end - elseif unireader.offset_x < unireader.min_offset_x then - unireader.offset_x = unireader.min_offset_x - end - elseif keydef.keycode == KEY_FW_UP then - unireader.offset_y = unireader.offset_y + y - if unireader.offset_y > 0 then - if unireader.pan_by_page then - unireader.show_overlap = unireader.offset_y + unireader.pan_overlap_vertical - end - unireader.offset_y = 0 - elseif unireader.pan_by_page then - unireader.show_overlap = unireader.pan_overlap_vertical -- bottom - end - elseif keydef.keycode == KEY_FW_DOWN then - unireader.offset_y = unireader.offset_y - y - if unireader.offset_y < unireader.min_offset_y then - if unireader.pan_by_page then - unireader.show_overlap = unireader.offset_y + y - unireader.min_offset_y - G_height - end - unireader.offset_y = unireader.min_offset_y - elseif unireader.pan_by_page then - unireader.show_overlap = -unireader.pan_overlap_vertical -- top - end - elseif keydef.keycode == KEY_FW_PRESS then - if keydef.modifier==MOD_SHIFT then - if unireader.pan_by_page then - unireader.offset_x = unireader.pan_x - unireader.offset_y = unireader.pan_y - else - unireader.offset_x = 0 - unireader.offset_y = 0 - end - else - unireader.pan_by_page = not unireader.pan_by_page - if unireader.pan_by_page then - unireader.pan_x = unireader.offset_x - unireader.pan_y = unireader.offset_y - end - end - end - if old_offset_x ~= unireader.offset_x - or old_offset_y ~= unireader.offset_y then - unireader:redrawCurrentPage() - end - end - end) - -- end panning - -- highlight mode - self.commands:add(KEY_N, nil, "N", - "start highlight mode", - function(unireader) - unireader:startHighLightMode() - unireader:goto(unireader.pageno) - end - ) - self.commands:add(KEY_N, MOD_SHIFT, "N", - "display all highlights", - function(unireader) - unireader:showHighLight() - unireader:goto(unireader.pageno) - end - ) - -- commands.map is very large, impacts startup performance on device - --debug("defined commands "..dump(self.commands.map)) -end diff --git a/wtest.lua b/wtest.lua index d0ed9dc60..f969d6a65 100644 --- a/wtest.lua +++ b/wtest.lua @@ -1,6 +1,8 @@ -require "ui" -require "readerui" -require "document" +print(package.path) +package.path = "./frontend/?.lua" +require "ui/ui" +require "ui/readerui" +require "document/document" TestGrid = Widget:new{}