From cb83e478758062e2f3f95aa31e52b943e97dada2 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sat, 7 Apr 2012 22:55:56 +0800 Subject: [PATCH] mod: use XPointer as absolute postion indicator inside document --- cre.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++++----- crereader.lua | 111 +++++++++++++++++++++++++++++++++++++-------- unireader.lua | 32 +++++++++---- 3 files changed, 226 insertions(+), 39 deletions(-) diff --git a/cre.cpp b/cre.cpp index bde307520..27260bd63 100644 --- a/cre.cpp +++ b/cre.cpp @@ -30,6 +30,7 @@ extern "C" { typedef struct CreDocument { LVDocView *text_view; + ldomDocument *dom_doc; } CreDocument; @@ -55,9 +56,9 @@ static int openDocument(lua_State *L) { doc->text_view->setViewMode(DVM_SCROLL, -1); doc->text_view->Resize(width, height); doc->text_view->LoadDocument(file_name); + doc->dom_doc = doc->text_view->getDocument(); doc->text_view->Render(); - printf("gamma: %d\n", fontMan->GetGammaIndex()); return 1; } @@ -99,7 +100,20 @@ static int getCurrentPage(lua_State *L) { return 1; } -static int getPos(lua_State *L) { +static int getPageFromXPointer(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + const char *xpointer_str = luaL_checkstring(L, 2); + + int page = 0; + ldomXPointer xp = doc->dom_doc->createXPointer(lString16(xpointer_str)); + + page = doc->text_view->getBookmarkPage(xp); + lua_pushinteger(L, page); + + return 1; +} + +static int getCurrentPos(lua_State *L) { CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); lua_pushinteger(L, doc->text_view->GetPos()); @@ -107,7 +121,22 @@ static int getPos(lua_State *L) { return 1; } -static int getPosPercent(lua_State *L) { +//static int getPosFromXPointer(lua_State *L) { + //CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + //const char *xpointer_str = luaL_checkstring(L, 2); + + //lvRect rc; + //int pos; + + //ldomXPointer *xp = NULL; + //xp = doc->dom_doc->createXPointer(lString16(xpointer_str)); + //getCursorDocRect(*xp, rc); + //pos = + + //return 1; +//} + +static int getCurrentPercent(lua_State *L) { CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); lua_pushinteger(L, doc->text_view->getPosPercent()); @@ -115,6 +144,15 @@ static int getPosPercent(lua_State *L) { return 1; } +static int getXPointer(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + ldomXPointer xp = doc->text_view->getBookmark(); + lua_pushstring(L, UnicodeToLocal(xp.toString()).c_str()); + + return 1; +} + static int getFullHeight(lua_State *L) { CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); @@ -138,7 +176,13 @@ static int walkTableOfContent(lua_State *L, LVTocItem *toc, int *count) { /* set subtable, Toc entry */ lua_newtable(L); lua_pushstring(L, "page"); - lua_pushnumber(L, toc_tmp->getY()); + lua_pushnumber(L, toc_tmp->getPercent()); + lua_settable(L, -3); + + lua_pushstring(L, "xpointer"); + lua_pushstring(L, UnicodeToLocal( + toc_tmp->getXPointer().toString()).c_str() + ); lua_settable(L, -3); lua_pushstring(L, "depth"); @@ -163,8 +207,18 @@ static int walkTableOfContent(lua_State *L, LVTocItem *toc, int *count) { /* * Return a table like this: * { - * {page=12, depth=1, title="chapter1"}, - * {page=54, depth=1, title="chapter2"}, + * { + * page=12, + * xpointer = "/body/DocFragment[11].0", + * depth=1, + * title="chapter1" + * }, + * { + * page=54, + * xpointer = "/body/DocFragment[13].0", + * depth=1, + * title="chapter2" + * }, * } * * Warnning: not like pdf or djvu support, page here refers to the @@ -246,6 +300,21 @@ static int gotoPos(lua_State *L) { return 0; } +static int gotoXPointer(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + const char *xpointer_str = luaL_checkstring(L, 2); + + ldomXPointer xp = doc->dom_doc->createXPointer(lString16(xpointer_str)); + + doc->text_view->goToBookmark(xp); + /* CREngine does not call checkPos() immediately after goToBookmark, + * so I have to manually update the pos in order to get a correctionColor + * return from GetPos() call. */ + doc->text_view->SetPos(xp.toPoint().y); + + return 0; +} + /* zoom font by given delta and return zoomed font size */ static int zoomFont(lua_State *L) { CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); @@ -265,6 +334,32 @@ static int toggleFontBolder(lua_State *L) { return 0; } +static int cursorRight(lua_State *L) { + //CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + //LVDocView *tv = doc->text_view; + + //ldomXPointer p = tv->getCurrentPageMiddleParagraph(); + //lString16 s = p.toString(); + //printf("~~~~~~~~~~%s\n", UnicodeToLocal(s).c_str()); + + //tv->selectRange(*(tv->selectFirstPageLink())); + //ldomXRange *r = tv->selectNextPageLink(true); + //lString16 s = r->getRangeText(); + //printf("------%s\n", UnicodeToLocal(s).c_str()); + + //tv->selectRange(*r); + //tv->updateSelections(); + + //LVPageWordSelector sel(doc->text_view); + //doc->text_view->doCommand(DCMD_SELECT_FIRST_SENTENCE); + //sel.moveBy(DIR_RIGHT, 2); + //sel.updateSelection(); + //printf("---------------- %s\n", UnicodeToLocal(sel.getSelectedWord()->getText()).c_str()); + + return 0; +} + static int drawCurrentPage(lua_State *L) { CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext"); @@ -306,21 +401,26 @@ static const struct luaL_Reg cre_func[] = { }; static const struct luaL_Reg credocument_meth[] = { - /* get methods */ + /*--- get methods ---*/ {"getPages", getNumberOfPages}, {"getCurrentPage", getCurrentPage}, - {"getPos", getPos}, - {"getPosPercent", getPosPercent}, + {"getPageFromXPointer", getPageFromXPointer}, + {"getCurrentPos", getCurrentPos}, + {"getCurrentPercent", getCurrentPercent}, + {"getXPointer", getXPointer}, {"getFullHeight", getFullHeight}, {"getToc", getTableOfContent}, - /* set methods */ + /*--- set methods ---*/ {"setFontFace", setFontFace}, - /* control methods */ + /* --- control methods ---*/ {"gotoPage", gotoPage}, {"gotoPercent", gotoPercent}, {"gotoPos", gotoPos}, + {"gotoXPointer", gotoXPointer}, {"zoomFont", zoomFont}, {"toggleFontBolder", toggleFontBolder}, + //{"cursorLeft", cursorLeft}, + //{"cursorRight", cursorRight}, {"drawCurrentPage", drawCurrentPage}, {"close", closeDocument}, {"__gc", closeDocument}, diff --git a/crereader.lua b/crereader.lua index 9b5358765..f51adbea7 100644 --- a/crereader.lua +++ b/crereader.lua @@ -3,7 +3,7 @@ require "inputbox" require "selectmenu" CREReader = UniReader:new{ - pos = 0, + pos = nil, percent = 0, gamma_index = 15, @@ -33,6 +33,9 @@ function CREReader:open(filename) return true end +---------------------------------------------------- +-- setting related methods +---------------------------------------------------- function CREReader:loadSpecialSettings() local font_face = self.settings:readSetting("font_face") self.font_face = font_face or "FreeSerif" @@ -46,7 +49,7 @@ end function CREReader:getLastPageOrPos() local last_percent = self.settings:readSetting("last_percent") if last_percent then - return (last_percent * self.doc:getFullHeight()) / 10000 + return math.floor((last_percent * self.doc:getFullHeight()) / 10000) else return 0 end @@ -61,24 +64,40 @@ 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:addJump(pos, notes) - return +function CREReader:redrawCurrentPage() + self:goto(self.pos) end -function CREReader:goto(pos) - local pos = math.min(pos, self.doc:getFullHeight()) - pos = math.max(pos, 0) +---------------------------------------------------- +-- goto related methods +---------------------------------------------------- +function CREReader:goto(pos, pos_type) + local prev_xpointer = self.doc:getXPointer() + if pos_type == "xpointer" then + self.doc:gotoXPointer(pos) + pos = self.doc:getCurrentPos() + else -- pos_type is PERCENT * 100 + pos = math.min(pos, self.doc:getFullHeight()) + pos = math.max(pos, 0) + self.doc:gotoPos(pos) + end -- add to jump_stack, 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 self.pos and math.abs(self.pos - pos) > height then - self:addJump(self.percent) + self:addJump(prev_xpointer) end - self.doc:gotoPos(pos) self.doc:drawCurrentPage(self.nulldc, fb.bb) if self.rcount == self.rcountmax then @@ -93,13 +112,65 @@ function CREReader:goto(pos) self.pos = pos self.pageno = self.doc:getCurrentPage() - self.percent = self.doc:getPosPercent() + self.percent = self.doc:getCurrentPercent() end -function CREReader:redrawCurrentPage() - self:goto(self.pos) +function CREReader:gotoPercent(percent) + self:goto(percent * self.doc:getFullHeight() / 10000) +end + +function CREReader:gotoTocEntry(entry) + self:goto(entry.xpointer, "xpointer") +end + +function CREReader:nextView() + return self.pos + height - self.pan_overlap_vertical +end + +function CREReader:prevView() + return self.pos - height + self.pan_overlap_vertical +end + +---------------------------------------------------- +-- jump stack related methods +---------------------------------------------------- +function CREReader:isSamePage(p1, p2) + return self.doc:getPageFromXPointer(p1) == self.doc:getPageFromXPointer(p2) end +function CREReader:showJumpStack() + local menu_items = {} + print(dump(self.jump_stack)) + for k,v in ipairs(self.jump_stack) do + table.insert(menu_items, + v.datetime.." -> page ".. + (self.doc:getPageFromXPointer(v.page)).." "..v.notes) + end + jump_menu = SelectMenu:new{ + menu_title = "Jump Keeper (current page: "..self.pageno..")", + item_array = menu_items, + no_item_msg = "No jump history.", + } + item_no = jump_menu:choose(0, fb.bb:getHeight()) + if item_no then + local jump_item = self.jump_stack[item_no] + self:goto(jump_item.page, "xpointer") + else + self:redrawCurrentPage() + end +end + +---------------------------------------------------- +-- TOC related methods +---------------------------------------------------- +function CREReader:getTocTitleOfCurrentPage() + return self:getTocTitleByPage(self.percent) +end + + +---------------------------------------------------- +-- menu related methods +---------------------------------------------------- -- used in CREReader:showMenu() function CREReader:_drawReadingInfo() local ypos = height - 50 @@ -109,7 +180,7 @@ function CREReader:_drawReadingInfo() ypos = ypos + 15 local face, fhash = Font:getFaceAndHash(22) - local cur_section = self:getTocTitleByPage(self.pos) + local cur_section = self:getTocTitleOfCurrentPage() if cur_section ~= "" then cur_section = "Section: "..cur_section end @@ -121,13 +192,7 @@ function CREReader:_drawReadingInfo() 5, 4, load_percent/100, 8) end -function CREReader:nextView() - return self.pos + height - self.pan_overlap_vertical -end -function CREReader:prevView() - return self.pos - height + self.pan_overlap_vertical -end function CREReader:adjustCreReaderCommands() -- delete commands @@ -204,6 +269,14 @@ function CREReader:adjustCreReaderCommands() cr:redrawCurrentPage() end ) + self.commands:add(KEY_BACK,nil,"back", + "back to last jump", + function(cr) + if #cr.jump_stack ~= 0 then + cr:goto(cr.jump_stack[1].page, "xpointer") + end + end + ) self.commands:add(KEY_VPLUS, nil, "vol+", "increase gamma", function(cr) diff --git a/unireader.lua b/unireader.lua index 4d010de80..f46f85d57 100644 --- a/unireader.lua +++ b/unireader.lua @@ -563,22 +563,27 @@ function UniReader:show(no) self.slot_visible = slot; end +function UniReader:isSamePage(p1, p2) + return p1 == p2 +end + --[[ @ pageno is the page you want to add to jump_stack + NOTE: for CREReader, pageno refers to xpointer --]] function UniReader:addJump(pageno, notes) local jump_item = nil local notes_to_add = notes if not notes_to_add then -- no notes given, auto generate from Toc entry - notes_to_add = self:getTocTitleByPage(self.pageno) + notes_to_add = self:getTocTitleOfCurrentPage() if notes_to_add ~= "" then notes_to_add = "in "..notes_to_add end end -- move pageno page to jump_stack top if already in for _t,_v in ipairs(self.jump_stack) do - if _v.page == pageno then + if self:isSamePage(_v.page, pageno) then jump_item = _v table.remove(self.jump_stack, _t) -- if original notes is not empty, probably defined by users, @@ -762,29 +767,38 @@ function UniReader:getTocTitleByPage(pageno) 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 when needed. + -- build toc if needed. self:fillToc() end - local menu_items = {} - local filtered_toc = {} + -- 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)) - table.insert(filtered_toc,v.page) end + toc_menu = SelectMenu:new{ menu_title = "Table of Contents", item_array = menu_items, no_item_msg = "This document does not have a Table of Contents.", } item_no = toc_menu:choose(0, fb.bb:getHeight()) + if item_no then - self:goto(filtered_toc[item_no]) + self:gotoTocEntry(self.toc[item_no]) else - self:goto(self.pageno) + self:redrawCurrentPage() end end @@ -849,7 +863,7 @@ function UniReader:_drawReadingInfo() local ypos = height - 50 fb.bb:paintRect(0, ypos, width, 50, 0) ypos = ypos + 15 - local cur_section = self:getTocTitleByPage(self.pageno) + local cur_section = self:getTocTitleOfCurrentPage() if cur_section ~= "" then cur_section = "Section: "..cur_section end