diff --git a/.gitignore b/.gitignore index 21f649e41..81a4bd850 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ kindlepdfviewer-*.zip /.project /.reader.kpdfview +kpvcrlib/CMakeCache.txt +kpvcrlib/CMakeFiles/ +kpvcrlib/cmake_install.cmake +kpvcrlib/Makefile diff --git a/.gitmodules b/.gitmodules index 77f6eecd4..ead63e79f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "djvulibre"] path = djvulibre url = git://djvu.git.sourceforge.net/gitroot/djvu/djvulibre.git +[submodule "kpvcrlib/crengine"] + path = kpvcrlib/crengine + url = git://crengine.git.sourceforge.net/gitroot/crengine/crengine diff --git a/Makefile b/Makefile index 710ead11c..8d9ca85a5 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,15 @@ MUPDFDIR=mupdf MUPDFTARGET=build/debug MUPDFLIBDIR=$(MUPDFDIR)/$(MUPDFTARGET) DJVUDIR=djvulibre +KPVCRLIGDIR=kpvcrlib +CRENGINEDIR=$(KPVCRLIGDIR)/crengine FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.8 LFSDIR=luafilesystem +# must point to directory with *.ttf fonts for crengine +TTF_FONTS_DIR=/usr/share/fonts/truetype/freefont/ + # set this to your ARM cross compiler: CC:=arm-unknown-linux-gnueabi-gcc @@ -55,15 +60,25 @@ KPDFREADER_CFLAGS=$(CFLAGS) -I$(LUADIR)/src -I$(MUPDFDIR)/ MUPDFLIBS := $(MUPDFLIBDIR)/libfitz.a DJVULIBS := $(DJVUDIR)/build/libdjvu/.libs/libdjvulibre.a +CRENGINELIBS := $(CRENGINEDIR)/crengine/libcrengine.a \ + $(CRENGINEDIR)/thirdparty/chmlib/libchmlib.a \ + $(CRENGINEDIR)/thirdparty/libpng/libpng.a \ + $(CRENGINEDIR)/thirdparty/antiword/libantiword.a THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \ - $(MUPDFLIBDIR)/libjpeg.a \ - $(MUPDFLIBDIR)/libopenjpeg.a \ - $(MUPDFLIBDIR)/libjbig2dec.a \ - $(MUPDFLIBDIR)/libz.a + $(MUPDFLIBDIR)/libopenjpeg.a \ + $(MUPDFLIBDIR)/libjbig2dec.a \ + $(MUPDFLIBDIR)/libjpeg.a \ + $(MUPDFLIBDIR)/libz.a + +#@TODO patch crengine to use the latest libjpeg 04.04 2012 (houqp) + #$(MUPDFLIBDIR)/libjpeg.a + #$(CRENGINEDIR)/thirdparty/libjpeg/libjpeg.a LUALIB := $(LUADIR)/src/liblua.a -kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) djvu.o +all:kpdfview slider_watcher + +kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) djvu.o $(DJVULIBS) cre.o $(CRENGINELIBS) $(CC) -lm -ldl -lpthread $(EMU_LDFLAGS) -lstdc++ \ kpdfview.o \ einkfb.o \ @@ -79,11 +94,16 @@ kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft $(LUALIB) \ djvu.o \ $(DJVULIBS) \ + cre.o \ + $(CRENGINELIBS) \ -o kpdfview einkfb.o input.o: %.o: %.c $(CC) -c $(KPDFREADER_CFLAGS) $(EMU_CFLAGS) $< -o $@ +slider_watcher: slider_watcher.c + $(CC) $(CFLAGS) $< -o $@ + ft.o: %.o: %.c $(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include $< -o $@ @@ -93,25 +113,38 @@ kpdfview.o pdf.o blitbuffer.o util.o drawcontext.o: %.o: %.c djvu.o: %.o: %.c $(CC) -c $(KPDFREADER_CFLAGS) -I$(DJVUDIR)/ $< -o $@ +cre.o: %.o: %.cpp + $(CC) -c -I$(CRENGINEDIR)/crengine/include/ -Ilua/src $< -o $@ -lstdc++ + lfs.o: $(LFSDIR)/src/lfs.c $(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@ fetchthirdparty: - -rm -Rf lua lua-5.1.4* + -rm -Rf lua lua-5.1.4 -rm -Rf mupdf/thirdparty git submodule init git submodule update + ln -sf kpvcrlib/crengine/cr3gui/data data + test -d fonts || ln -sf $(TTF_FONTS_DIR) fonts test -f mupdf-thirdparty.zip || wget http://www.mupdf.com/download/mupdf-thirdparty.zip unzip mupdf-thirdparty.zip -d mupdf + cd mupdf/thirdparty/jpeg-*/ && \ + patch -N -p0 < ../../../kpvcrlib/jpeg_compress_struct_size.patch &&\ + patch -N -p0 < ../../../kpvcrlib/jpeg_decompress_struct_size.patch test -f lua-5.1.4.tar.gz || wget http://www.lua.org/ftp/lua-5.1.4.tar.gz tar xvzf lua-5.1.4.tar.gz && ln -s lua-5.1.4 lua clean: - -rm -f *.o kpdfview + -rm -f *.o kpdfview slider_watcher cleanthirdparty: make -C $(LUADIR) clean make -C $(MUPDFDIR) clean + make -C $(CRENGINEDIR)/thirdparty/antiword clean + make -C $(CRENGINEDIR)/thirdparty/chmlib clean + make -C $(CRENGINEDIR)/thirdparty/libpng clean + make -C $(CRENGINEDIR)/crengine clean + make -C $(KPVCRLIGDIR) clean -rm -rf $(DJVUDIR)/build -rm -f $(MUPDFDIR)/fontdump.host -rm -f $(MUPDFDIR)/cmapdump.host @@ -139,10 +172,15 @@ else endif make -C $(DJVUDIR)/build +$(CRENGINELIBS): + cd $(KPVCRLIGDIR) && rm -rf CMakeCache.txt CMakeFiles && \ + CFLAGS="$(CFLAGS)" CC="$(CC)" CXX="$(CXX)" cmake . && \ + make + $(LUALIB): make -C lua/src CC="$(CC)" CFLAGS="$(CFLAGS)" MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E" liblua.a -thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) +thirdparty: $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) $(CRENGINELIBS) INSTALL_DIR=kindlepdfviewer @@ -156,7 +194,9 @@ customupdate: kpdfview # ensure that build binary is for ARM file kpdfview | grep ARM || exit 1 mkdir $(INSTALL_DIR) - cp -p README.TXT COPYING kpdfview *.lua $(INSTALL_DIR) + cp -p README.TXT COPYING kpdfview slider_watcher *.lua $(INSTALL_DIR) + cp -rpL data $(INSTALL_DIR) + cp -rp fonts $(INSTALL_DIR) zip -r kindlepdfviewer-$(VERSION).zip $(INSTALL_DIR) launchpad/ rm -Rf $(INSTALL_DIR) @echo "copy kindlepdfviewer-$(VERSION).zip to /mnt/us/customupdates and install with shift+shift+I" diff --git a/commands.lua b/commands.lua index a6622ac43..1d7064b03 100644 --- a/commands.lua +++ b/commands.lua @@ -5,6 +5,7 @@ Keydef = { modifier = nil, descr = nil } + function Keydef:_new(obj) -- obj definition obj = obj or {} @@ -13,6 +14,7 @@ function Keydef:_new(obj) self.__tostring=Keydef.tostring return obj end + function Keydef:new(keycode,modifier,descr) obj = Keydef:_new() obj.keycode = keycode @@ -20,13 +22,16 @@ function Keydef:new(keycode,modifier,descr) obj.descr = descr return obj end + function Keydef:display() return ((self.modifier and 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, @@ -34,6 +39,7 @@ Command = { help = nil, order = nil } + function Command:_new(obj) -- obj definition obj = obj or {} @@ -42,6 +48,7 @@ function Command:_new(obj) self.__tostring=Command.tostring return obj end + function Command:new(keydef, func, help, keygroup, order) obj = Command:_new() obj.keydef = keydef @@ -52,6 +59,7 @@ function Command:new(keydef, func, help, keygroup, order) --print("creating command: ["..tostring(keydef).."] keygroup:["..(keygroup or "").."] help:"..help) return obj end + function Command:tostring() return tostring(self.keydef)..": "..(self.help or "") end @@ -61,6 +69,7 @@ Commands = { map = {}, size = 0 } + function Commands:add(keycode,modifier,keydescr,help,func) if type(keycode) == "table" then for i=1,#keycode,1 do @@ -72,11 +81,42 @@ function Commands:add(keycode,modifier,keydescr,help,func) 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) @@ -95,25 +135,32 @@ function Commands:_addImpl(keydef,help,func,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 = {} - setmetatable(self.map,mt) - mt.__index=function (table, key) + mt.__index = function(table, key) return rawget(table,(key.modifier or "").."@#@"..(key.keycode or "")) end - mt.__newindex=function (table, key, value) + mt.__newindex = function(table, key, value) return rawset(table,(key.modifier or "").."@#@"..(key.keycode or ""),value) end - -- obj definition - obj = obj or {} - setmetatable(obj, self) - self.__index = self + setmetatable(obj.map, mt) + return obj end diff --git a/cre.cpp b/cre.cpp new file mode 100644 index 000000000..27260bd63 --- /dev/null +++ b/cre.cpp @@ -0,0 +1,464 @@ +/* + KindlePDFViewer: CREngine abstraction for Lua + Copyright (C) 2012 Hans-Werner Hilse + Qingping Hou + + 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 . +*/ + + +extern "C" { +#include "blitbuffer.h" +#include "drawcontext.h" +#include "cre.h" +} + +#include "crengine.h" + +//using namespace std; + +typedef struct CreDocument { + LVDocView *text_view; + ldomDocument *dom_doc; +} CreDocument; + + +static int openDocument(lua_State *L) { + const char *file_name = luaL_checkstring(L, 1); + const char *style_sheet = luaL_checkstring(L, 2); + + int width = luaL_checkint(L, 3); + int height = luaL_checkint(L, 4); + lString8 css; + + CreDocument *doc = (CreDocument*) lua_newuserdata(L, sizeof(CreDocument)); + luaL_getmetatable(L, "credocument"); + lua_setmetatable(L, -2); + + doc->text_view = new LVDocView(); + doc->text_view->setBackgroundColor(0x000000); + if (LVLoadStylesheetFile(lString16(style_sheet), css)){ + if (!css.empty()){ + doc->text_view->setStyleSheet(css); + } + } + 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(); + + + return 1; +} + +static int getGammaIndex(lua_State *L) { + lua_pushinteger(L, fontMan->GetGammaIndex()); + + return 1; +} + +static int setGammaIndex(lua_State *L) { + int index = luaL_checkint(L, 1); + + fontMan->SetGammaIndex(index); + + return 0; +} + +static int closeDocument(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + delete doc->text_view; + + return 0; +} + +static int getNumberOfPages(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + lua_pushinteger(L, doc->text_view->getPageCount()); + + return 1; +} + +static int getCurrentPage(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + lua_pushinteger(L, doc->text_view->getCurPage()); + + return 1; +} + +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()); + + return 1; +} + +//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()); + + 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"); + + lua_pushinteger(L, doc->text_view->GetFullHeight()); + + return 1; +} + +/* + * helper function for getTableOfContent() + */ +static int walkTableOfContent(lua_State *L, LVTocItem *toc, int *count) { + LVTocItem *toc_tmp = NULL; + int i = 0, + nr_child = toc->getChildCount(); + + for(i = 0; i < nr_child; i++) { + toc_tmp = toc->getChild(i); + lua_pushnumber(L, (*count)++); + + /* set subtable, Toc entry */ + lua_newtable(L); + lua_pushstring(L, "page"); + 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"); + lua_pushnumber(L, toc_tmp->getLevel()); + lua_settable(L, -3); + + lua_pushstring(L, "title"); + lua_pushstring(L, UnicodeToLocal(toc_tmp->getName()).c_str()); + lua_settable(L, -3); + + + /* set Toc entry to Toc table */ + lua_settable(L, -3); + + if (toc_tmp->getChildCount() > 0) { + walkTableOfContent(L, toc_tmp, count); + } + } + return 0; +} + +/* + * Return a table like this: + * { + * { + * 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 + * percent of height within the document, not the real page number. + * We use page here just to keep consistent with other readers. + * + */ +static int getTableOfContent(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + LVTocItem * toc = doc->text_view->getToc(); + int count = 0; + + lua_newtable(L); + walkTableOfContent(L, toc, &count); + + return 1; +} + +/* + * Return a table like this: + * { + * "FreeMono", + * "FreeSans", + * "FreeSerif", + * } + * + */ +static int getFontFaces(lua_State *L) { + int i = 0; + lString16Collection face_list; + + fontMan->getFaceList(face_list); + + lua_newtable(L); + for (i = 0; i < face_list.length(); i++) + { + lua_pushnumber(L, i+1); + lua_pushstring(L, UnicodeToLocal(face_list[i]).c_str()); + lua_settable(L, -3); + } + + return 1; +} + +static int setFontFace(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + const char *face = luaL_checkstring(L, 2); + + doc->text_view->setDefaultFontFace(lString8(face)); + + return 0; +} + +static int gotoPage(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + int pageno = luaL_checkint(L, 2); + + doc->text_view->goToPage(pageno); + + return 0; +} + +static int gotoPercent(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + int percent = luaL_checkint(L, 2); + + doc->text_view->SetPos(percent * doc->text_view->GetFullHeight() / 10000); + + return 0; +} + +static int gotoPos(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + int pos = luaL_checkint(L, 2); + + doc->text_view->SetPos(pos); + + 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"); + int delta = luaL_checkint(L, 2); + + doc->text_view->ZoomFont(delta); + + lua_pushnumber(L, doc->text_view->getFontSize()); + return 1; +} + +static int toggleFontBolder(lua_State *L) { + CreDocument *doc = (CreDocument*) luaL_checkudata(L, 1, "credocument"); + + doc->text_view->doCommand(DCMD_TOGGLE_BOLD); + + 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"); + BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 3, "blitbuffer"); + + int w = bb->w, + h = bb->h; + LVGrayDrawBuf drawBuf(w, h, 8); + + doc->text_view->Resize(w, h); + doc->text_view->Render(); + doc->text_view->Draw(drawBuf); + + + uint8_t *bbptr = (uint8_t*)bb->data; + uint8_t *pmptr = (uint8_t*)drawBuf.GetScanLine(0); + int i,x; + + for (i = 0; i < h; i++) { + for (x = 0; x < (bb->w / 2); x++) { + bbptr[x] = 255 - (((pmptr[x*2 + 1] & 0xF0) >> 4) | + (pmptr[x*2] & 0xF0)); + } + if(bb->w & 1) { + bbptr[x] = 255 - (pmptr[x*2] & 0xF0); + } + bbptr += bb->pitch; + pmptr += w; + } +} + + +static const struct luaL_Reg cre_func[] = { + {"openDocument", openDocument}, + {"getFontFaces", getFontFaces}, + {"getGammaIndex", getGammaIndex}, + {"setGammaIndex", setGammaIndex}, + {NULL, NULL} +}; + +static const struct luaL_Reg credocument_meth[] = { + /*--- get methods ---*/ + {"getPages", getNumberOfPages}, + {"getCurrentPage", getCurrentPage}, + {"getPageFromXPointer", getPageFromXPointer}, + {"getCurrentPos", getCurrentPos}, + {"getCurrentPercent", getCurrentPercent}, + {"getXPointer", getXPointer}, + {"getFullHeight", getFullHeight}, + {"getToc", getTableOfContent}, + /*--- set methods ---*/ + {"setFontFace", setFontFace}, + /* --- control methods ---*/ + {"gotoPage", gotoPage}, + {"gotoPercent", gotoPercent}, + {"gotoPos", gotoPos}, + {"gotoXPointer", gotoXPointer}, + {"zoomFont", zoomFont}, + {"toggleFontBolder", toggleFontBolder}, + //{"cursorLeft", cursorLeft}, + //{"cursorRight", cursorRight}, + {"drawCurrentPage", drawCurrentPage}, + {"close", closeDocument}, + {"__gc", closeDocument}, + {NULL, NULL} +}; + +int luaopen_cre(lua_State *L) { + luaL_newmetatable(L, "credocument"); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); + luaL_register(L, NULL, credocument_meth); + lua_pop(L, 1); + luaL_register(L, "cre", cre_func); + + + /* initialize fonts for CREngine */ + InitFontManager(lString8("./fonts")); + + lString8 fontDir("./fonts"); + LVContainerRef dir = LVOpenDirectory( LocalToUnicode(fontDir).c_str() ); + if ( !dir.isNull() ) + for ( int i=0; iGetObjectCount(); i++ ) { + const LVContainerItemInfo * item = dir->GetObjectInfo(i); + lString16 fileName = item->GetName(); + if ( !item->IsContainer() && fileName.length()>4 && lString16(fileName, fileName.length()-4, 4)==L".ttf" ) { + lString8 fn = UnicodeToLocal(fileName); + printf("loading font: %s\n", fn.c_str()); + if ( !fontMan->RegisterFont(fn) ) { + printf(" failed\n"); + } + } + } + +#ifdef DEBUG_CRENGINE + CRLog::setStdoutLogger(); + CRLog::setLogLevel(CRLog::LL_DEBUG); +#endif + + return 1; +} diff --git a/cre.h b/cre.h new file mode 100644 index 000000000..f4166095e --- /dev/null +++ b/cre.h @@ -0,0 +1,28 @@ +/* + KindlePDFViewer: CREngine abstraction for Lua + Copyright (C) 2012 Hans-Werner Hilse + Qingping Hou + + 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 . +*/ + +#ifndef _CRENGING_H +#define _CRENGING_H + +#include +#include +#include + +int luaopen_cre(lua_State *L); +#endif diff --git a/crereader.lua b/crereader.lua new file mode 100644 index 000000000..698a0d2b5 --- /dev/null +++ b/crereader.lua @@ -0,0 +1,314 @@ +require "unireader" +require "inputbox" +require "selectmenu" + +CREReader = UniReader:new{ + pos = nil, + percent = 0, + + gamma_index = 15, + font_face = nil, +} + +function CREReader:init() + self:addAllCommands() + self:adjustCreReaderCommands() +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, + width, height) + if not ok then + return false, self.doc -- will contain error message + end + + 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) +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) +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 + +---------------------------------------------------- +-- 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(prev_xpointer) + end + + self.doc:drawCurrentPage(self.nulldc, fb.bb) + + if self.rcount == self.rcountmax then + print("full refresh") + self.rcount = 1 + fb:refresh(0) + else + print("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, "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 + local load_percent = self.percent/100 + + fb.bb:paintRect(0, ypos, width, 50, 0) + + ypos = ypos + 15 + local face, fhash = Font:getFaceAndHash(22) + local cur_section = self:getTocTitleOfCurrentPage() + if cur_section ~= "" then + cur_section = "Section: "..cur_section + end + renderUtf8Text(fb.bb, 10, ypos+6, face, fhash, + "Position: "..load_percent.."%".." "..cur_section, true) + + ypos = ypos + 15 + blitbuffer.progressBar(fb.bb, 10, ypos, 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, nil, "J") + self.commands:del(KEY_K, nil, "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_F, MOD_SHIFT, "F") + self.commands:del(KEY_F, MOD_ALT, "F") + + -- overwrite commands + self.commands:add(KEY_PGFWD, MOD_SHIFT_OR_ALT, ">", + "increase font size", + function(cr) + cr.doc:zoomFont(1) + cr:redrawCurrentPage() + end + ) + self.commands:add(KEY_PGBCK, MOD_SHIFT_OR_ALT, "<", + "decrease font size", + function(cr) + cr.doc:zoomFont(-1) + cr: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(cr, keydef) + print('jump to position: '.. + math.floor(cr.doc:getFullHeight()*(keydef.keycode-KEY_1)/9).. + '/'..cr.doc:getFullHeight()) + cr:goto(math.floor(cr.doc:getFullHeight()*(keydef.keycode-KEY_1)/9)) + end + ) + self.commands:add(KEY_F, nil, "F", + "invoke font menu", + function(cr) + 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, height) + print(face_list[item_no]) + if item_no then + cr.doc:setFontFace(face_list[item_no]) + self.font_face = face_list[item_no] + end + cr:redrawCurrentPage() + end + ) + self.commands:add(KEY_F, MOD_ALT, "F", + "Toggle font bolder attribute", + function(cr) + cr.doc:toggleFontBolder() + cr:redrawCurrentPage() + end + ) + self.commands:add(KEY_B, MOD_SHIFT, "B", + "add jump", + function(cr) + cr:addJump(self.doc:getXPointer()) + 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) + cre.setGammaIndex(self.gamma_index + 1) + self.gamma_index = cre.getGammaIndex() + cr:redrawCurrentPage() + end + ) + self.commands:add(KEY_VMINUS, nil, "vol-", + "decrease gamma", + function(cr) + cre.setGammaIndex(self.gamma_index - 1) + self.gamma_index = cre.getGammaIndex() + cr:redrawCurrentPage() + end + ) + self.commands:add(KEY_FW_UP, nil, "joypad up", + "pan "..self.shift_y.." pixels upwards", + function(cr) + cr:goto(cr.pos - cr.shift_y) + end + ) + self.commands:add(KEY_FW_DOWN, nil, "joypad down", + "pan "..self.shift_y.." pixels downwards", + function(cr) + cr:goto(cr.pos + cr.shift_y) + end + ) +end diff --git a/djvu.c b/djvu.c index a97a4f610..56a34c32f 100644 --- a/djvu.c +++ b/djvu.c @@ -473,7 +473,7 @@ static const struct luaL_Reg djvu_func[] = { static const struct luaL_Reg djvudocument_meth[] = { {"openPage", openPage}, {"getPages", getNumberOfPages}, - {"getTOC", getTableOfContent}, + {"getToc", getTableOfContent}, {"getPageText", getPageText}, {"close", closeDocument}, {"getCacheSize", getCacheSize}, diff --git a/djvureader.lua b/djvureader.lua index be72ec4c0..c9ec0cdf4 100644 --- a/djvureader.lua +++ b/djvureader.lua @@ -13,6 +13,27 @@ function DJVUReader:open(filename) return ok end +function DJVUReader:init() + self:addAllCommands() + self:adjustDjvuReaderCommand() +end + +function DJVUReader:adjustDjvuReaderCommand() + 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 + ) +end -----------[ highlight support ]---------- diff --git a/filechooser.lua b/filechooser.lua index b8ea6d3b9..cb5a5aaf5 100644 --- a/filechooser.lua +++ b/filechooser.lua @@ -54,7 +54,11 @@ function FileChooser:readDir() 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" then + 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 == "fb2" or file_type == "chm" then table.insert(self.files, f) end end diff --git a/filesearcher.lua b/filesearcher.lua index f2792c268..31ec6e371 100644 --- a/filesearcher.lua +++ b/filesearcher.lua @@ -30,10 +30,16 @@ function FileSearcher:readDir() 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 string.match(f, ".+%.[pP][dD][fF]$") or string.match(f, ".+%.[dD][jJ][vV][uU]$") then + 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 == "fb2" or file_type == "chm" then file_entry = {dir=d, name=f,} table.insert(self.files, file_entry) --print("file:"..d.."/"..f) diff --git a/input.c b/input.c index 05eedaa49..2f7853d2c 100644 --- a/input.c +++ b/input.c @@ -22,8 +22,8 @@ #include #include "input.h" -#define NUM_FDS 3 -int inputfds[3] = { -1, -1, -1 }; +#define NUM_FDS 4 +int inputfds[4] = { -1, -1, -1, -1 }; static int openInputDevice(lua_State *L) { #ifndef EMULATE_READER diff --git a/keys.lua b/keys.lua index 489a93e05..0c155282e 100644 --- a/keys.lua +++ b/keys.lua @@ -87,6 +87,9 @@ 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 diff --git a/kpdfview.c b/kpdfview.c index d14a38053..a09a9a0ab 100644 --- a/kpdfview.c +++ b/kpdfview.c @@ -26,6 +26,8 @@ #include "blitbuffer.h" #include "drawcontext.h" #include "pdf.h" +#include "djvu.h" +#include "cre.h" #include "einkfb.h" #include "input.h" #include "ft.h" @@ -53,6 +55,7 @@ int main(int argc, char **argv) { luaopen_einkfb(L); luaopen_pdf(L); luaopen_djvu(L); + luaopen_cre(L); luaopen_input(L); luaopen_util(L); luaopen_ft(L); diff --git a/kpvcrlib/CMakeLists.txt b/kpvcrlib/CMakeLists.txt new file mode 100644 index 000000000..19bafa56b --- /dev/null +++ b/kpvcrlib/CMakeLists.txt @@ -0,0 +1,51 @@ +PROJECT(kpvcrlib) +cmake_minimum_required(VERSION 2.6) + +SET(MUPDF_DIR ../mupdf) +SET(MUPDF_3RDPARTY_DIR ${MUPDF_DIR}/thirdparty) +SET(CR_3RDPARTY_DIR crengine/thirdparty) + +SET(CR3_PNG 1) +#SET(CR3_JPEG 1) + +SET(FREETYPE_INCLUDE_DIRS ${CR_3RDPARTY_DIR}/freetype/include) +SET(ANTIWORD_INCLUDE_DIR ${CR_3RDPARTY_DIR}/antiword) +SET(CHM_INCLUDE_DIRS ${CR_3RDPARTY_DIR}/chmlib) +SET(PNG_INCLUDE_DIR ${CR_3RDPARTY_DIR}/libpng) +SET(ZLIB_INCLUDE_DIR ${MUPDF_3RDPARTY_DIR}/zlib-1.2.5) +#SET(ZLIB_INCLUDE_DIR ${CR_3RDPARTY_DIR}/zlib) +SET(JPEGLIB_INCLUDE_DIR ${MUPDF_3RDPARTY_DIR}/jpeg-8d) +#SET(JPEGLIB_INCLUDE_DIR ${CR_3RDPARTY_DIR}/libjpeg) +SET(JCONFIG_INCLUDE_DIR ${MUPDF_DIR}/scripts) + +INCLUDE_DIRECTORIES( + ${FREETYPE_INCLUDE_DIRS} + ${ANTIWORD_INCLUDE_DIR} + ${CHM_INCLUDE_DIRS} + ${PNG_INCLUDE_DIR} + ${ZLIB_INCLUDE_DIR} + ${JPEGLIB_INCLUDE_DIR} + ${JCONFIG_INCLUDE_DIR} +) + +ADD_DEFINITIONS(-DUSE_FONTCONFIG=0 -DUSE_FREETYPE=1 -DCR3_PATCH=1 -DNDEBUG=1) + +message("Will build patched LIBCHM library") +ADD_DEFINITIONS(-DCHM_SUPPORT_ENABLED=1) +ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/chmlib) + +message("Will build patched LIBPNG library") +ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/libpng) + +#message("Will build patched JPEGLIB library") +#ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/libjpeg) + +message("Will build patched ANTIWORD library") +ADD_DEFINITIONS(-DENABLE_ANTIWORD=1) +ADD_DEFINITIONS(-DCR3_ANTIWORD_PATCH=1) +ADD_SUBDIRECTORY(${CR_3RDPARTY_DIR}/antiword) + +SET(GUI kpv) +#ADD_DEFINITIONS(-DJCONFIG_INCLUDED=1) +ADD_SUBDIRECTORY(crengine/crengine) + diff --git a/kpvcrlib/crengine b/kpvcrlib/crengine new file mode 160000 index 000000000..ba469d334 --- /dev/null +++ b/kpvcrlib/crengine @@ -0,0 +1 @@ +Subproject commit ba469d33473670ca303e2ef7f9762452a86e18b1 diff --git a/kpvcrlib/jpeg_compress_struct_size.patch b/kpvcrlib/jpeg_compress_struct_size.patch new file mode 100644 index 000000000..e2da6de22 --- /dev/null +++ b/kpvcrlib/jpeg_compress_struct_size.patch @@ -0,0 +1,15 @@ +--- jcapimin.c 2012-04-04 00:02:30.000000000 +0800 ++++ jcapimin-patched.c 2012-04-04 00:02:26.000000000 +0800 +@@ -36,9 +36,9 @@ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); +- if (structsize != SIZEOF(struct jpeg_compress_struct)) +- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, +- (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); ++ /*if (structsize != SIZEOF(struct jpeg_compress_struct))*/ ++ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */ ++ /*(int) SIZEOF(struct jpeg_compress_struct), (int) structsize);*/ + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set diff --git a/kpvcrlib/jpeg_decompress_struct_size.patch b/kpvcrlib/jpeg_decompress_struct_size.patch new file mode 100644 index 000000000..deaf375a1 --- /dev/null +++ b/kpvcrlib/jpeg_decompress_struct_size.patch @@ -0,0 +1,15 @@ +--- jdapimin.c 2012-04-04 01:09:00.000000000 +0800 ++++ jdapimin-patched.c 2012-04-04 01:42:44.000000000 +0800 +@@ -36,9 +36,9 @@ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); +- if (structsize != SIZEOF(struct jpeg_decompress_struct)) +- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, +- (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize); ++ /*if (structsize != SIZEOF(struct jpeg_decompress_struct))*/ ++ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */ ++ /*(int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);*/ + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set diff --git a/launchpad/kpdf.sh b/launchpad/kpdf.sh index 6d1eba1a4..e43bc5c04 100755 --- a/launchpad/kpdf.sh +++ b/launchpad/kpdf.sh @@ -1,8 +1,18 @@ #!/bin/sh +SLIDER_EVENT_PIPE="/tmp/event_slider" export LC_ALL="en_US.UTF-8" echo unlock > /proc/keypad echo unlock > /proc/fiveway cd /mnt/us/kindlepdfviewer/ -./reader.lua $1 + +# create the named pipe for power slider event +if [ ! -p $SLIDER_EVENT_PIPE ]; then + mkfifo $SLIDER_EVENT_PIPE +fi +killall slider_watcher +./slider_watcher $SLIDER_EVENT_PIPE & + +./reader.lua $1 2> /mnt/us/kindlepdfviewer/crash.log +killall -cont cvm echo 1 > /proc/eink_fb/update_display diff --git a/pdf.c b/pdf.c index aa98c8626..87190f14d 100644 --- a/pdf.c +++ b/pdf.c @@ -445,7 +445,7 @@ static const struct luaL_Reg pdfdocument_meth[] = { {"authenticatePassword", authenticatePassword}, {"openPage", openPage}, {"getPages", getNumberOfPages}, - {"getTOC", getTableOfContent}, + {"getToc", getTableOfContent}, {"close", closeDocument}, {"getCacheSize", getCacheSize}, {"cleanCache", cleanCache}, diff --git a/reader.lua b/reader.lua index c197d55ee..193eb768d 100755 --- a/reader.lua +++ b/reader.lua @@ -20,6 +20,7 @@ require "alt_getopt" require "pdfreader" require "djvureader" +require "crereader" require "filechooser" require "settings" require "screen" @@ -42,12 +43,14 @@ function openFile(filename) 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" then + reader = CREReader end if reader then local ok, err = reader:open(filename) if ok then reader:loadSettings(filename) - page_num = reader.settings:readSetting("last_page") or 1 + page_num = reader:getLastPageOrPos() reader:goto(tonumber(page_num)) reader_settings:savesetting("lastfile", filename) return reader:inputLoop() @@ -92,6 +95,7 @@ if optarg["d"] == "k3" then input.open("/dev/input/event0") input.open("/dev/input/event1") input.open("/dev/input/event2") + input.open("/tmp/event_slider") setK3Keycodes() elseif optarg["d"] == "emu" then input.open("") @@ -100,6 +104,7 @@ elseif optarg["d"] == "emu" then else input.open("/dev/input/event0") input.open("/dev/input/event1") + input.open("/tmp/event_slider") -- check if we are running on Kindle 3 (additional volume input) local f=lfs.attributes("/dev/input/event2") @@ -119,7 +124,7 @@ fb = einkfb.open("/dev/fb0") width, height = fb:getSize() -- read current rotation mode Screen:updateRotationMode() -origin_rotation_mode = Screen.cur_rotation_mode +Screen.native_rotation_mode = Screen.cur_rotation_mode -- set up reader's setting: font reader_settings = DocSettings:open(".reader") @@ -133,6 +138,7 @@ UniReader:initGlobalSettings(reader_settings) -- initialize specific readers PDFReader:init() DJVUReader:init() +CREReader:init() -- display directory or open file local patharg = reader_settings:readSetting("lastfile") @@ -167,10 +173,10 @@ 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(origin_rotation_mode) +fb:setOrientation(Screen.native_rotation_mode) input.closeAll() ---os.execute('test -e /proc/keypad && echo "send '..KEY_HOME..'" > /proc/keypad ') if optarg["d"] ~= "emu" then + --os.execute("killall -cont cvm") os.execute('echo "send '..KEY_MENU..'" > /proc/keypad;echo "send '..KEY_MENU..'" > /proc/keypad') end diff --git a/screen.lua b/screen.lua index 24923a2e7..6d6022498 100644 --- a/screen.lua +++ b/screen.lua @@ -41,6 +41,9 @@ Codes for rotation modes: Screen = { cur_rotation_mode = 0, + -- these two variabls are used to help switching from framework to reader + native_rotation_mode = nil, + kpv_rotation_mode = nil, } -- @orien: 1 for clockwise rotate, -1 for anti-clockwise @@ -71,4 +74,3 @@ function Screen:updateRotationMode() end end - diff --git a/selectmenu.lua b/selectmenu.lua index 3572a5c99..c4ce67f45 100644 --- a/selectmenu.lua +++ b/selectmenu.lua @@ -2,6 +2,7 @@ require "rendertext" require "keys" require "graphics" require "font" +require "commands" SelectMenu = { -- font for displaying item names @@ -32,10 +33,14 @@ SelectMenu = { "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) @@ -46,10 +51,12 @@ function SelectMenu:new(o) 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 @@ -62,43 +69,164 @@ function SelectMenu:getItemIndexByShortCut(c, perpage) end end ---[ --- return the index of selected item ---] -function SelectMenu:choose(ypos, height) - local perpage = math.floor(height / self.spacing) - 2 - local pagedirty = true - local markerdirty = false - - local prevItem = function () - if self.current == 1 then - if self.page > 1 then - self.current = perpage - self.page = self.page - 1 - pagedirty = true +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 - else - self.current = self.current - 1 - markerdirty = true - end - 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) - 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 + 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 - 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) + + 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 + end) + + self.commands:add(KEY_FW_PRESS, nil, "", + "select menu item", + function(sm) + if sm.last_shortcut < 30 then + if sm.items == 0 then + return "break" + else + self.selected_item = (sm.perpage * (sm.page - 1) + + sm.current) + end + end + end) + + local KEY_Q_to_E = {} + for i = KEY_Q, KEY_P do + table.insert(KEY_Q_to_E, Keydef:new(i, nil, "")) + end + self.commands:addGroup("Q to E", KEY_Q_to_E, + "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 @@ -106,8 +234,8 @@ function SelectMenu:choose(ypos, height) local tface, tfhash = Font:getFaceAndHash(25, Font.tfont) local fface, ffhash = Font:getFaceAndHash(16, Font.ffont) - if pagedirty then - markerdirty = true + 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) @@ -125,11 +253,12 @@ function SelectMenu:choose(ypos, height) y = y + self.spacing renderUtf8Text(fb.bb, 30, y, cface, cfhash, self.no_item_msg, true) - markerdirty = false + self.markerdirty = false + self:clearCommands() else local c - for c = 1, perpage do - local i = (self.page - 1) * perpage + 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) @@ -153,19 +282,21 @@ function SelectMenu:choose(ypos, height) renderUtf8Text(fb.bb, 50, y, cface, cfhash, self.item_array[i], true) - end - end - end + end -- EOF if i <= self.items + end -- EOF for + end -- EOF if -- draw footer - y = ypos + self.title_H + (self.spacing * perpage) + self.foot_H + 5 + 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, ffhash, - "Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true) + "Page "..self.page.." of ".. + (math.ceil(self.items / self.perpage)), true) end - if markerdirty then - if not pagedirty then + 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) @@ -175,72 +306,41 @@ function SelectMenu:choose(ypos, height) -- 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 pagedirty then + if not self.pagedirty then fb:refresh(1, 45, y, fb.bb:getWidth() - 60, 3) end self.oldcurrent = self.current - markerdirty = false + self.markerdirty = false end - if pagedirty then + if self.pagedirty then fb:refresh(0, 0, ypos, fb.bb:getWidth(), height) - pagedirty = false + self.pagedirty = false end local ev = input.waitForEvent() ev.code = adjustKeyEvents(ev) if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then - local selected = nil - if ev.code == KEY_FW_UP then - prevItem() - elseif ev.code == KEY_FW_DOWN then - nextItem() - 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_FW_PRESS or ev.code == KEY_ENTER and self.last_shortcut < 30 then - if self.items == 0 then - return nil - else - return (perpage*(self.page-1) + self.current) - end - elseif ev.code >= KEY_Q and ev.code <= KEY_P then - selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_Q + 1 ], perpage) - elseif ev.code >= KEY_A and ev.code <= KEY_L then - selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_A + 11], perpage) - elseif ev.code >= KEY_Z and ev.code <= KEY_M then - selected = self:getItemIndexByShortCut(self.item_shortcuts[ ev.code - KEY_Z + 21], perpage) - elseif ev.code == KEY_DEL then - selected = self:getItemIndexByShortCut("Del", perpage) - elseif ev.code == KEY_DOT then - selected = self:getItemIndexByShortCut(".", perpage) - elseif ev.code == KEY_SYM or ev.code == KEY_SLASH then -- DXG has slash after dot - selected = self:getItemIndexByShortCut("Sym", perpage) - elseif ev.code == KEY_ENTER then - selected = self:getItemIndexByShortCut("Ent", perpage) - elseif ev.code == KEY_BACK then - return nil + keydef = Keydef:new(ev.code, getKeyModifier()) + print("key pressed: "..tostring(keydef)) + + command = self.commands:getByKeydef(keydef) + if command ~= nil then + print("command to execute: "..tostring(command)) + ret_code = command.func(self, keydef) + else + print("command not found: "..tostring(command)) end - if selected ~= nil then - print("# selected "..selected) - return selected + + if ret_code == "break" then + break end - end - end + + if self.selected_item ~= nil then + print("# selected "..self.selected_item) + return self.selected_item + end + end -- EOF if + end -- EOF while + return nil end diff --git a/slider_watcher.c b/slider_watcher.c new file mode 100644 index 000000000..6a4e26e15 --- /dev/null +++ b/slider_watcher.c @@ -0,0 +1,87 @@ +/* + KindlePDFViewer: power slider key event watcher + Copyright (C) 2012 Qingping Hou + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OUTPUT_SIZE 21 +#define EVENT_PIPE "/tmp/event_slider" +#define CODE_IN_SAVER 10000 +#define CODE_OUT_SAVER 10001 + +int +main ( int argc, char *argv[] ) +{ + int fd, ret; + FILE *fp; + char std_out[OUTPUT_SIZE] = ""; + struct input_event ev; + __u16 key_code = 10000; + + /* create the npipe if not exists */ + /*if(access(argv[1], F_OK)){*/ + /*printf("npipe %s not found, try to create it...\n", argv[1]);*/ + /*if(mkfifo(argv[1], 0777)) {*/ + /*printf("Create npipe %s failed!\n", argv[1]);*/ + /*}*/ + /*}*/ + + /* open npipe for writing */ + fd = open(argv[1], O_RDWR | O_NONBLOCK); + if(fd < 0) { + printf("Open %s falied: %s\n", argv[1], strerror(errno)); + exit(EXIT_FAILURE); + } + + /* initialize event struct */ + ev.type = EV_KEY; + ev.code = key_code; + ev.value = 1; + + while(1) { + /* listen power slider events */ + memset(std_out, 0, OUTPUT_SIZE); + fp = popen("lipc-wait-event -s 0 com.lab126.powerd goingToScreenSaver,outOfScreenSaver", "r"); + ret = fread(std_out, OUTPUT_SIZE, 1, fp); + pclose(fp); + + /* fill event struct */ + gettimeofday(&ev.time, NULL); + if(std_out[0] == 'g') { + ev.code = CODE_IN_SAVER; + } else if(std_out[0] == 'o') { + ev.code = CODE_OUT_SAVER; + } else { + printf("Unrecognized event.\n"); + exit(EXIT_FAILURE); + } + + /* generate event */ + ret = write(fd, &ev, sizeof(struct input_event)); + } + + close(fd); + return EXIT_SUCCESS; +} diff --git a/unireader.lua b/unireader.lua index f2ecd3626..c0a3d42d5 100644 --- a/unireader.lua +++ b/unireader.lua @@ -94,22 +94,23 @@ end -- !!!!!!!!!!!!!!!!!!!!!!!!! -- -- For a new specific reader, --- you must always overwrite following two methods: +-- you must always overwrite following method: -- -- * self:open() --- * self:init() -- -- overwrite other methods if needed. ---------------------------------------------------- -function UniReader:init() -end --- open a file and its settings store --- tips: you can use self:loadSettings in open() method. +-- open a file function UniReader:open(filename, cache_size) return false end +function UniReader:init() + -- initialize commands + self:addAllCommands() +end + ---------------------------------------------------- -- You need to overwrite following two methods if your -- reader supports highlight feature. @@ -128,7 +129,7 @@ function UniReader:toggleTextHighLight(word_list) end ---------------------------------------------------- --- renderer memory +-- Renderer memory ---------------------------------------------------- function UniReader:getCacheSize() @@ -139,9 +140,46 @@ 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_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 + +-- This is a low-level method that can be shared with all readers. function UniReader:loadSettings(filename) if self.doc ~= nil then self.settings = DocSettings:open(filename,self.cache_document_size) @@ -164,33 +202,18 @@ function UniReader:loadSettings(filename) self.globalzoom = self.settings:readSetting("globalzoom") or 1.0 self.globalzoommode = self.settings:readSetting("globalzoommode") or -1 + self:loadSpecialSettings() return true end return false end -function UniReader:initGlobalSettings(settings) - local pan_overlap_vertical = settings:readSetting("pan_overlap_vertical") - if pan_overlap_vertical then - self.pan_overlap_vertical = pan_overlap_vertical - end - -- initialize commands - self:addAllCommands() - - 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 +function UniReader:getLastPageOrPos() + return self.settings:readSetting("last_page") or 1 +end - local rcountmax = settings:readSetting("partial_refresh_count") - if rcountmax then - self.rcountmax = rcountmax - end +function UniReader:saveLastPageOrPos() + self.settings:savesetting("last_page", self.pageno) end -- guarantee that we have enough memory in cache @@ -282,7 +305,7 @@ function UniReader:drawOrCache(no, preCache) tile.y = 0 tile.w = self.fullwidth tile.h = self.fullheight - elseif (tile.w*tile.h / 2) > max_cache then + 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 @@ -297,7 +320,7 @@ function UniReader:drawOrCache(no, preCache) tile.y = tile.y - 5 tile.h = tile.h + 5 end - if tile.y + tile.h < self.fullheigth then + if tile.y + tile.h < self.fullheight then tile.h = tile.h + 5 end end @@ -347,6 +370,8 @@ function UniReader:setzoom(page, preCache) 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 @@ -540,27 +565,32 @@ 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) + -- no notes given, auto generate from Toc entry + 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, -- we use the original notes to overwrite auto generated notes - -- from TOC entry + -- from Toc entry if jump_item.notes ~= "" then notes_to_add = jump_item.notes end @@ -618,6 +648,10 @@ function UniReader:goto(no) end end +function UniReader:redrawCurrentPage() + self:goto(self.pageno) +end + function UniReader:nextView() local pageno = self.pageno @@ -673,14 +707,14 @@ end function UniReader:modifyGamma(factor) print("modifyGamma, gamma="..self.globalgamma.." factor="..factor) self.globalgamma = self.globalgamma * factor; - self:goto(self.pageno) + self:redrawCurrentPage() end -- adjust zoom state and trigger re-rendering function UniReader:setGlobalZoomMode(newzoommode) if self.globalzoommode ~= newzoommode then self.globalzoommode = newzoommode - self:goto(self.pageno) + self:redrawCurrentPage() end end @@ -689,13 +723,13 @@ function UniReader:setGlobalZoom(zoom) if self.globalzoom ~= zoom then self.globalzoommode = self.ZOOM_BY_VALUE self.globalzoom = zoom - self:goto(self.pageno) + self:redrawCurrentPage() end end function UniReader:setRotate(rotate) self.globalrotate = rotate - self:goto(self.pageno) + self:redrawCurrentPage() end -- @ orien: 1 for clockwise rotate, -1 for anti-clockwise @@ -703,21 +737,21 @@ function UniReader:screenRotate(orien) Screen:screenRotate(orien) width, height = fb:getSize() self:clearCache() - self:goto(self.pageno) + self:redrawCurrentPage() end -function UniReader:cleanUpTOCTitle(title) +function UniReader:cleanUpTocTitle(title) return title:gsub("\13", "") end -function UniReader:fillTOC() - self.toc = self.doc:getTOC() +function UniReader:fillToc() + self.toc = self.doc:getToc() end -function UniReader:getTOCTitleByPage(pageno) +function UniReader:getTocTitleByPage(pageno) if not self.toc then -- build toc when needed. - self:fillTOC() + self:fillToc() end -- no table of content @@ -732,32 +766,41 @@ function UniReader:getTOCTitleByPage(pageno) end pre_entry = _v end - return self:cleanUpTOCTitle(pre_entry.title) + return self:cleanUpTocTitle(pre_entry.title) end -function UniReader:showTOC() +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. - self:fillTOC() + -- 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) + (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title)) end + toc_menu = SelectMenu:new{ menu_title = "Table of Contents", item_array = menu_items, no_item_msg = "This document does not have a Table of Contents.", } item_no = toc_menu:choose(0, fb.bb:getHeight()) + if item_no then - self:goto(filtered_toc[item_no]) + self:gotoTocEntry(self.toc[item_no]) else - self:goto(self.pageno) + self:redrawCurrentPage() end end @@ -777,7 +820,7 @@ function UniReader:showJumpStack() local jump_item = self.jump_stack[item_no] self:goto(jump_item.page) else - self:goto(self.pageno) + self:redrawCurrentPage() end end @@ -804,7 +847,9 @@ function UniReader:showHighLight() end end -function UniReader:showMenu() +-- used in UniReader:showMenu() +function UniReader:_drawReadingInfo() + local ypos = height - 50 local load_percent = (self.pageno / self.doc:getPages()) local face, fhash = Font:getFaceAndHash(22) @@ -820,7 +865,7 @@ function UniReader:showMenu() 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 @@ -831,6 +876,10 @@ function UniReader:showMenu() 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 @@ -889,13 +938,14 @@ function UniReader:inputLoop() self.doc:close() end if self.settings ~= nil then - self.settings:savesetting("last_page", self.pageno) + self:saveLastPageOrPos() self.settings:savesetting("gamma", self.globalgamma) self.settings:savesetting("jumpstack", self.jump_stack) self.settings:savesetting("bbox", self.bbox) self.settings:savesetting("globalzoom", self.globalzoom) self.settings:savesetting("globalzoommode", self.globalzoommode) self.settings:savesetting("highlight", self.highlight) + self:saveSpecialSettings() self.settings:close() end @@ -1025,12 +1075,12 @@ function UniReader:addAllCommands() "show help page", function(unireader) HelpPage:show(0,height,unireader.commands) - unireader:goto(unireader.pageno) + unireader:redrawCurrentPage() end) self.commands:add(KEY_T,nil,"T", "show table of content", function(unireader) - unireader:showTOC() + unireader:showToc() end) self.commands:add(KEY_B,nil,"B", "show jump stack", @@ -1062,18 +1112,6 @@ function UniReader:addAllCommands() function(unireader) unireader:screenRotate("anticlockwise") end) - 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) self.commands:add(KEY_R, MOD_SHIFT, "R", "manual full screen refresh", function(unireader) @@ -1118,7 +1156,7 @@ function UniReader:addAllCommands() "open menu", function(unireader) unireader:showMenu() - unireader:goto(unireader.pageno) + 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)} @@ -1206,10 +1244,25 @@ function UniReader:addAllCommands() end if old_offset_x ~= unireader.offset_x or old_offset_y ~= unireader.offset_y then - unireader:goto(unireader.pageno) + unireader:redrawCurrentPage() end end end) -- end panning + self.commands:add(KEY_INTO_SCREEN_SAVER,nil,"slider", + "toggle screen saver", + function(unireader) + Screen.kpv_rotation_mode = Screen.cur_rotation_mode + fb:setOrientation(Screen.native_rotation_mode) + os.execute("killall -cont cvm") + end) + self.commands:add(KEY_OUTOF_SCREEN_SAVER,nil,"slider", + "toggle screen saver", + function(unireader) + os.execute("sleep 1") + os.execute("killall -stop cvm") + fb:setOrientation(Screen.kpv_rotation_mode) + unireader:redrawCurrentPage() + end) print("## defined commands "..dump(self.commands.map)) end