mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Merge branch 'djvu-highlight' into epub
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,10 +3,13 @@ lua
|
||||
lua-*
|
||||
.reader.kpdfview.lua
|
||||
mupdf-thirdparty.zip
|
||||
djvulibre*
|
||||
kpdfview
|
||||
*.o
|
||||
|
||||
kindlepdfviewer-*.zip
|
||||
|
||||
/.cproject
|
||||
/.project
|
||||
/.reader.kpdfview
|
||||
|
||||
|
||||
58
blitbuffer.c
58
blitbuffer.c
@@ -366,6 +366,63 @@ static int paintRect(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int invertRect(lua_State *L) {
|
||||
BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer");
|
||||
int x = luaL_checkint(L, 2);
|
||||
int y = luaL_checkint(L, 3);
|
||||
int w = luaL_checkint(L, 4);
|
||||
int h = luaL_checkint(L, 5);
|
||||
uint8_t *dstptr;
|
||||
|
||||
int cy, cx;
|
||||
if(w <= 0 || h <= 0 || x >= dst->w || y >= dst->h) {
|
||||
return 0;
|
||||
}
|
||||
if(x + w > dst->w) {
|
||||
w = dst->w - x;
|
||||
}
|
||||
if(y + h > dst->h) {
|
||||
h = dst->h - y;
|
||||
}
|
||||
|
||||
if(x & 1) {
|
||||
/* This will invert the leftmost column
|
||||
* in the case when x is odd. After this,
|
||||
* x will become even. */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
*dstptr ^= 0x0F;
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
x++;
|
||||
w--;
|
||||
}
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
x / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
for(cx = 0; cx < w/2; cx++) {
|
||||
*(dstptr+cx) ^= 0xFF;
|
||||
}
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
if(w & 1) {
|
||||
/* This will invert the rightmost column
|
||||
* in the case when (w & 1) && !(x & 1) or
|
||||
* !(w & 1) && (x & 1). */
|
||||
dstptr = (uint8_t*)(dst->data +
|
||||
y * dst->pitch +
|
||||
(x + w) / 2);
|
||||
for(cy = 0; cy < h; cy++) {
|
||||
*dstptr ^= 0xF0;
|
||||
dstptr += dst->pitch;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg blitbuffer_func[] = {
|
||||
{"new", newBlitBuffer},
|
||||
{NULL, NULL}
|
||||
@@ -378,6 +435,7 @@ static const struct luaL_Reg blitbuffer_meth[] = {
|
||||
{"addblitFrom", addblitToBuffer},
|
||||
{"blitFullFrom", blitFullToBuffer},
|
||||
{"paintRect", paintRect},
|
||||
{"invertRect", invertRect},
|
||||
{"free", freeBlitBuffer},
|
||||
{"__gc", freeBlitBuffer},
|
||||
{NULL, NULL}
|
||||
|
||||
125
djvu.c
125
djvu.c
@@ -206,7 +206,7 @@ static int openPage(lua_State *L) {
|
||||
static int getPageSize(lua_State *L) {
|
||||
DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
|
||||
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
||||
|
||||
|
||||
lua_pushnumber(L, dc->zoom * page->info.width);
|
||||
lua_pushnumber(L, dc->zoom * page->info.height);
|
||||
|
||||
@@ -225,6 +225,128 @@ static int getUsedBBox(lua_State *L) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a table like following:
|
||||
* {
|
||||
* -- a line entry
|
||||
* 1 = {
|
||||
* 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 3 = {word="Word", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 4 = {word="List", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089,
|
||||
* },
|
||||
*
|
||||
* -- an other line entry
|
||||
* 2 = {
|
||||
* 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089},
|
||||
* x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089,
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
static int getPageText(lua_State *L) {
|
||||
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
|
||||
int pageno = luaL_checkint(L, 2);
|
||||
|
||||
miniexp_t sexp, se_line, se_word;
|
||||
int i = 1, j = 1, counter_l = 1, counter_w=1,
|
||||
nr_line = 0, nr_word = 0;
|
||||
const char *word = NULL;
|
||||
|
||||
while ((sexp = ddjvu_document_get_pagetext(doc->doc_ref, pageno-1, "word"))
|
||||
== miniexp_dummy) {
|
||||
handle(L, doc->context, True);
|
||||
}
|
||||
|
||||
/* throuw page info and obtain lines info, after this, sexp's entries
|
||||
* are lines. */
|
||||
sexp = miniexp_cdr(sexp);
|
||||
/* get number of lines in a page */
|
||||
nr_line = miniexp_length(sexp);
|
||||
/* table that contains all the lines */
|
||||
lua_newtable(L);
|
||||
|
||||
counter_l = 1;
|
||||
for(i = 1; i <= nr_line; i++) {
|
||||
/* retrive one line entry */
|
||||
se_line = miniexp_nth(i, sexp);
|
||||
nr_word = miniexp_length(se_line);
|
||||
if(nr_word == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* subtable that contains words in a line */
|
||||
lua_pushnumber(L, counter_l);
|
||||
lua_newtable(L);
|
||||
counter_l++;
|
||||
|
||||
/* set line position */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(2, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(4, se_line)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* now loop through each word in the line */
|
||||
counter_w = 1;
|
||||
for(j = 1; j <= nr_word; j++) {
|
||||
/* retrive one word entry */
|
||||
se_word = miniexp_nth(j, se_line);
|
||||
/* check to see whether the entry is empty */
|
||||
word = miniexp_to_str(miniexp_nth(5, se_word));
|
||||
if (!word) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* create table that contains info for a word */
|
||||
lua_pushnumber(L, counter_w);
|
||||
lua_newtable(L);
|
||||
counter_w++;
|
||||
|
||||
/* set word info */
|
||||
lua_pushstring(L, "x0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y0");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(2, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "x1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y1");
|
||||
lua_pushnumber(L, miniexp_to_int(miniexp_nth(4, se_word)));
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "word");
|
||||
lua_pushstring(L, word);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* set word entry to line subtable */
|
||||
lua_settable(L, -3);
|
||||
} /* end of for (j) */
|
||||
|
||||
/* set line entry to page text table */
|
||||
lua_settable(L, -3);
|
||||
} /* end of for (i) */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int closePage(lua_State *L) {
|
||||
DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
|
||||
if(page->page_ref != NULL) {
|
||||
@@ -334,6 +456,7 @@ static const struct luaL_Reg djvudocument_meth[] = {
|
||||
{"openPage", openPage},
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getTOC", getTableOfContent},
|
||||
{"getPageText", getPageText},
|
||||
{"close", closeDocument},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
|
||||
605
djvureader.lua
605
djvureader.lua
@@ -12,3 +12,608 @@ function DJVUReader:open(filename)
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
|
||||
|
||||
-----------[ highlight support ]----------
|
||||
|
||||
----------------------------------------------------
|
||||
-- Given coordinates of four conners and return
|
||||
-- coordinate of upper left conner with with and height
|
||||
--
|
||||
-- In djvulibre library, some coordinates starts from
|
||||
-- down left conner, i.e. y is upside down. This method
|
||||
-- only transform these coordinates.
|
||||
----------------------------------------------------
|
||||
function DJVUReader:_rectCoordTransform(x0, y0, x1, y1)
|
||||
return
|
||||
self.offset_x + x0 * self.globalzoom,
|
||||
self.offset_y + self.cur_full_height - (y1 * self.globalzoom),
|
||||
(x1 - x0) * self.globalzoom,
|
||||
(y1 - y0) * self.globalzoom
|
||||
end
|
||||
|
||||
-- make sure the whole word can be seen in screen
|
||||
function DJVUReader:_isEntireWordInScreenRange(w)
|
||||
return self:_isEntireWordInScreenHeightRange(w) and
|
||||
self:_isEntireWordInScreenWidthRange(w)
|
||||
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 + height)
|
||||
end
|
||||
|
||||
function DJVUReader:_isEntireWordInScreenWidthRange(w)
|
||||
return (w ~= nil) and
|
||||
(w.x0 * self.globalzoom >= -self.offset_x) and
|
||||
(w.x1 * self.globalzoom <= -self.offset_x + width)
|
||||
end
|
||||
|
||||
-- make sure at least part of the word can be seen in screen
|
||||
function DJVUReader:_isWordInScreenRange(w)
|
||||
return (w ~= nil) and
|
||||
(self.cur_full_height - (w.y0 * self.globalzoom) >=
|
||||
-self.offset_y) and
|
||||
(self.cur_full_height - (w.y1 * self.globalzoom) <=
|
||||
-self.offset_y + height) and
|
||||
(w.x1 * self.globalzoom >= -self.offset_x) and
|
||||
(w.x0 * self.globalzoom <= -self.offset_x + width)
|
||||
end
|
||||
|
||||
function DJVUReader: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:_isEntireWordInScreenHeightRange(line_item) then
|
||||
local x, y, w, h = self:_rectCoordTransform(
|
||||
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 -- EOF if isEntireWordInScreenHeightRange
|
||||
end -- EOF for line_item
|
||||
end -- EOF for text_item
|
||||
end
|
||||
|
||||
function DJVUReader:_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 -- EOF closure
|
||||
end
|
||||
|
||||
function DJVUReader:_toggleWordHighLight(t, l, w)
|
||||
x, y, w, h = self:_rectCoordTransform(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 DJVUReader:_toggleTextHighLight(t, l0, w0, l1, w1)
|
||||
--print("# toggle 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 DJVUReader:drawCursorAfterWord(t, l, w)
|
||||
self.cursor:setHeight((t[l].y1 - t[l].y0) * self.globalzoom)
|
||||
self.cursor:moveTo(
|
||||
self.offset_x + t[l][w].x1 * self.globalzoom,
|
||||
self.offset_y + self.cur_full_height - (t[l].y1 * self.globalzoom))
|
||||
self.cursor:draw()
|
||||
end
|
||||
|
||||
function DJVUReader:drawCursorBeforeWord(t, l, w)
|
||||
self.cursor:setHeight((t[l].y1 - t[l].y0)
|
||||
* self.globalzoom)
|
||||
self.cursor:moveTo(
|
||||
self.offset_x + t[l][w].x0 * self.globalzoom - self.cursor.w,
|
||||
self.offset_y + self.cur_full_height - t[l].y1 * self.globalzoom)
|
||||
self.cursor:draw()
|
||||
end
|
||||
|
||||
function DJVUReader:startHighLightMode()
|
||||
local t = self.doc:getPageText(self.pageno)
|
||||
|
||||
local function _findFirstWordInView(t)
|
||||
for i=1, #t, 1 do
|
||||
if self:_isEntireWordInScreenRange(t[i][1]) then
|
||||
return i, 1
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
local function _isMovingForward(l, w)
|
||||
return l.cur > l.start or (l.cur == l.start and w.cur > w.start)
|
||||
end
|
||||
|
||||
local l = {}
|
||||
local w = {}
|
||||
|
||||
l.start, w.start = _findFirstWordInView(t)
|
||||
if not l.start then
|
||||
print("# no text in current view!")
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
self.cursor = Cursor:new {
|
||||
x_pos = t[l.cur][w.cur].x1*self.globalzoom,
|
||||
y_pos = self.offset_y + (self.cur_full_height
|
||||
- (t[l.cur][w.cur].y1 * self.globalzoom)),
|
||||
h = (t[l.cur][w.cur].y1 - t[l.cur][w.cur].y0) * self.globalzoom,
|
||||
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.waitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
if ev.code == KEY_FW_LEFT then
|
||||
if w.cur == 1 then
|
||||
w.cur = 0
|
||||
w.new = 0
|
||||
else
|
||||
if w.cur == 0 then
|
||||
-- already at the left end of current line,
|
||||
-- goto previous line (_prevWord does not understand
|
||||
-- zero w.cur)
|
||||
w.cur = 1
|
||||
end
|
||||
l.new, w.new = _prevWord(t, l.cur, w.cur)
|
||||
end
|
||||
|
||||
self.cursor:clear()
|
||||
if w.new ~= 0
|
||||
and not self:_isEntireWordInScreenHeightRange(t[l.new][w.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.cur == 0 then
|
||||
-- meet line left end, must be handled as special case
|
||||
if self:_isEntireWordInScreenRange(t[l.cur][1]) then
|
||||
self:drawCursorBeforeWord(t, l.cur, 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 then
|
||||
if w.cur == 0 then
|
||||
w.cur = 1
|
||||
w.new = 1
|
||||
else
|
||||
l.new, w.new = _nextWord(t, l.cur, w.cur)
|
||||
if w.new == 1 then
|
||||
-- Must be come from the right end of previous line,
|
||||
-- so goto the left end of current line.
|
||||
w.cur = 0
|
||||
w.new = 0
|
||||
end
|
||||
end
|
||||
|
||||
self.cursor:clear()
|
||||
|
||||
local tmp_w = w.new
|
||||
if w.cur == 0 then
|
||||
tmp_w = 1
|
||||
end
|
||||
if not self:_isEntireWordInScreenHeightRange(t[l.new][tmp_w])
|
||||
and self:_isEntireWordInScreenWidthRange(t[l.new][tmp_w]) then
|
||||
local pageno = self:nextView()
|
||||
self:goto(pageno)
|
||||
end
|
||||
|
||||
if w.cur == 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 then
|
||||
if w.cur == 0 then
|
||||
-- goto left end of last line
|
||||
l.new = math.max(l.cur - 1, 1)
|
||||
elseif l.cur == 1 and w.cur == 1 then
|
||||
-- already first word, to the left end of first line
|
||||
w.new = 0
|
||||
else
|
||||
l.new, w.new = _wordInPrevLine(t, l.cur, w.cur)
|
||||
end
|
||||
|
||||
self.cursor:clear()
|
||||
|
||||
local tmp_w = w.new
|
||||
if w.cur == 0 then
|
||||
tmp_w = 1
|
||||
end
|
||||
if not self:_isEntireWordInScreenHeightRange(t[l.new][tmp_w])
|
||||
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 then
|
||||
if w.cur == 0 then
|
||||
-- on the left end of current line,
|
||||
-- goto left end of next line
|
||||
l.new = math.min(l.cur + 1, #t)
|
||||
else
|
||||
l.new, w.new = _wordInNextLine(t, l.cur, w.cur)
|
||||
end
|
||||
|
||||
self.cursor:clear()
|
||||
|
||||
local tmp_w = w.new
|
||||
if w.cur == 0 then
|
||||
tmp_w = 1
|
||||
end
|
||||
if not self:_isEntireWordInScreenHeightRange(t[l.new][tmp_w])
|
||||
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
|
||||
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
|
||||
end
|
||||
end -- EOF for line_item
|
||||
end -- EOF for text_item
|
||||
end -- EOF if not highlight table
|
||||
if #self.highlight[self.pageno] == 0 then
|
||||
self.highlight[self.pageno] = nil
|
||||
end
|
||||
return
|
||||
elseif ev.code == KEY_FW_PRESS then
|
||||
if w.cur == 0 then
|
||||
w.cur = 1
|
||||
l.cur, w.cur = _prevWord(t, l.cur, w.cur)
|
||||
end
|
||||
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 -- EOF if key event
|
||||
l.cur, w.cur = l.new, w.new
|
||||
fb:refresh(1)
|
||||
end
|
||||
end -- EOF while
|
||||
--print("start", l.cur, w.cur, l.start, w.start)
|
||||
|
||||
-- two helper functions for highlight
|
||||
local function _togglePrevWordHighLight(t, l, w)
|
||||
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
|
||||
not self:_isEntireWordInScreenHeightRange(t[l.new][w.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 not self:_isEntireWordInScreenHeightRange(t[l.new][w.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.waitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS 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 -- EOF if is 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
|
||||
elseif ev.code == KEY_FW_DOWN then
|
||||
is_meet_start = false
|
||||
if not is_meet_end then
|
||||
if w.cur == 0 then
|
||||
-- handle left end of first line as special case
|
||||
tmp_l = math.min(tmp_l + 1, #t)
|
||||
tmp_w = 1
|
||||
else
|
||||
tmp_l, tmp_w = _wordInNextLine(t, l.new, w.new)
|
||||
end
|
||||
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 -- EOF if key event
|
||||
fb:refresh(1)
|
||||
end
|
||||
end -- EOF while
|
||||
end
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ function FileChooser:choose(ypos, height)
|
||||
end
|
||||
end
|
||||
pagedirty = true
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
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
|
||||
@@ -208,7 +208,7 @@ function FileChooser:choose(ypos, height)
|
||||
self.current = self.items - (self.page-1)*perpage
|
||||
markerdirty = true
|
||||
end
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
pagedirty = true
|
||||
|
||||
106
graphics.lua
106
graphics.lua
@@ -6,6 +6,7 @@ blitbuffer.paintBorder = function (bb, x, y, w, h, bw, c)
|
||||
bb:paintRect(x+w-bw, y+bw, bw, h - 2*bw, c)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Draw a progress bar according to following args:
|
||||
|
||||
@@ -27,3 +28,108 @@ blitbuffer.progressBar = function (bb, x, y, w, h,
|
||||
fb.bb:paintRect(x+load_m_w, y+load_m_h,
|
||||
(w-2*load_m_w)*load_percent, (h-2*load_m_h), c)
|
||||
end
|
||||
|
||||
|
||||
|
||||
------------------------------------------------
|
||||
-- Start of Cursor class
|
||||
------------------------------------------------
|
||||
|
||||
Cursor = {
|
||||
x_pos = 0,
|
||||
y_pos = 0,
|
||||
--color = 15,
|
||||
h = 10,
|
||||
w = nil,
|
||||
line_w = nil,
|
||||
is_cleared = true,
|
||||
}
|
||||
|
||||
function Cursor:new(o)
|
||||
o = o or {}
|
||||
o.x_pos = o.x_pos or self.x_pos
|
||||
o.y_pos = o.y_pos or self.y_pos
|
||||
o.line_width_factor = o.line_width_factor or 10
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
o:setHeight(o.h or self.h)
|
||||
return o
|
||||
end
|
||||
|
||||
function Cursor:setHeight(h)
|
||||
self.h = h
|
||||
self.w = self.h / 3
|
||||
self.line_w = math.floor(self.h / self.line_width_factor)
|
||||
end
|
||||
|
||||
function Cursor:_draw(x, y)
|
||||
local up_down_width = math.floor(self.line_w / 2)
|
||||
local body_h = self.h - (up_down_width * 2)
|
||||
-- paint upper horizontal line
|
||||
fb.bb:invertRect(x, y, self.w, up_down_width)
|
||||
-- paint middle vertical line
|
||||
fb.bb:invertRect(x + (self.w / 2) - up_down_width, y + up_down_width,
|
||||
self.line_w, body_h)
|
||||
-- paint lower horizontal line
|
||||
fb.bb:invertRect(x, y + body_h + up_down_width, self.w, up_down_width)
|
||||
end
|
||||
|
||||
function Cursor:draw()
|
||||
if self.is_cleared then
|
||||
self.is_cleared = false
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:clear()
|
||||
if not self.is_cleared then
|
||||
self.is_cleared = true
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:move(x_off, y_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontal(x_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
end
|
||||
|
||||
function Cursor:moveVertical(x_off)
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveAndDraw(x_off, y_off)
|
||||
self:clear()
|
||||
self:move(x_off, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveTo(x_pos, y_pos)
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
end
|
||||
|
||||
function Cursor:moveToAndDraw(x_pos, y_pos)
|
||||
self:clear()
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontalAndDraw(x_off)
|
||||
self:clear()
|
||||
self:move(x_off, 0)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveVerticalAndDraw(y_off)
|
||||
self:clear()
|
||||
self:move(0, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
|
||||
148
inputbox.lua
148
inputbox.lua
@@ -4,6 +4,8 @@ require "graphics"
|
||||
|
||||
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
|
||||
@@ -15,44 +17,77 @@ InputBox = {
|
||||
shiftmode = false,
|
||||
altmode = false,
|
||||
|
||||
cursor = nil,
|
||||
|
||||
-- font for displaying input content
|
||||
-- we have to use mono here for better distance controlling
|
||||
face = freetype.newBuiltinFace("mono", 25),
|
||||
fhash = "m25",
|
||||
fheight = 25,
|
||||
fwidth = 16,
|
||||
fwidth = 15,
|
||||
}
|
||||
|
||||
function InputBox:setDefaultInput(text)
|
||||
self.input_string = ""
|
||||
self:addString(text)
|
||||
--self.input_cur_x = self.input_start_x + (string.len(text) * self.fwidth)
|
||||
--self.input_string = text
|
||||
end
|
||||
|
||||
function InputBox:addString(str)
|
||||
for i = 1, #str do
|
||||
self:addChar(str:sub(i,i))
|
||||
end
|
||||
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.fhash,
|
||||
self.input_string, 0)
|
||||
end
|
||||
|
||||
function InputBox:addChar(char)
|
||||
renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, self.face, self.fhash,
|
||||
char, true)
|
||||
fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight)
|
||||
self.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
|
||||
self.input_string = self.input_string .. char
|
||||
-- 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(0,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:drawBox(ypos, w, h, title)
|
||||
@@ -66,35 +101,40 @@ function InputBox:drawBox(ypos, w, h, title)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
|| d_text default to nil (used to set default text in input slot)
|
||||
--]]
|
||||
----------------------------------------------------------------------
|
||||
-- InputBox:input()
|
||||
--
|
||||
-- @title: input prompt for the box
|
||||
-- @d_text: default to nil (used to set default text in input slot)
|
||||
----------------------------------------------------------------------
|
||||
function InputBox:input(ypos, height, title, d_text)
|
||||
local pagedirty = true
|
||||
-- do some initilization
|
||||
self.h = height
|
||||
self.input_start_y = ypos + 35
|
||||
self.input_cur_x = self.input_start_x
|
||||
self.input_slot_w = fb.bb:getWidth() - 170
|
||||
|
||||
if d_text then -- if specified default text, draw it
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
self:setDefaultInput(d_text)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
else -- otherwise, leave the draw task to the main loop
|
||||
self.input_string = ""
|
||||
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:drawBox(ypos, w, h, title)
|
||||
if d_text then
|
||||
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
|
||||
self.cursor:draw()
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
|
||||
while true do
|
||||
if pagedirty then
|
||||
w = fb.bb:getWidth() - 40
|
||||
h = height - 45
|
||||
self:drawBox(ypos, w, h, title)
|
||||
fb:refresh(1, 20, ypos, w, h)
|
||||
pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.waitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
|
||||
@@ -177,14 +217,30 @@ function InputBox:input(ypos, height, title, d_text)
|
||||
self:addChar(" ")
|
||||
elseif ev.code == KEY_PGFWD then
|
||||
elseif ev.code == KEY_PGBCK then
|
||||
elseif ev.code == KEY_FW_LEFT then
|
||||
if (self.cursor.x_pos + 3) > self.input_start_x then
|
||||
self.cursor:moveHorizontalAndDraw(-self.fwidth)
|
||||
fb:refresh(1, self.input_start_x-5, ypos,
|
||||
self.input_slot_w, h)
|
||||
end
|
||||
elseif ev.code == KEY_FW_RIGHT then
|
||||
if (self.cursor.x_pos + 3) < self.input_cur_x then
|
||||
self.cursor:moveHorizontalAndDraw(self.fwidth)
|
||||
fb:refresh(1,self.input_start_x-5, ypos,
|
||||
self.input_slot_w, h)
|
||||
end
|
||||
elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then
|
||||
if self.input_string == "" then
|
||||
self.input_string = nil
|
||||
end
|
||||
break
|
||||
elseif ev.code == KEY_DEL then
|
||||
self:delChar()
|
||||
elseif ev.code == KEY_BACK then
|
||||
if Keys.shiftmode then
|
||||
self:clearText()
|
||||
else
|
||||
self:delChar()
|
||||
end
|
||||
elseif ev.code == KEY_BACK or ev.code == KEY_HOME then
|
||||
self.input_string = nil
|
||||
break
|
||||
end
|
||||
@@ -195,5 +251,7 @@ function InputBox:input(ypos, height, title, d_text)
|
||||
end -- if
|
||||
end -- while
|
||||
|
||||
return self.input_string
|
||||
local return_str = self.input_string
|
||||
self.input_string = ""
|
||||
return return_str
|
||||
end
|
||||
|
||||
@@ -150,6 +150,7 @@ if ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "directory" then
|
||||
else
|
||||
if file ~= nil then
|
||||
running = openFile(file)
|
||||
print(file)
|
||||
else
|
||||
running = false
|
||||
end
|
||||
|
||||
BIN
test/2col.pdf
Normal file
BIN
test/2col.pdf
Normal file
Binary file not shown.
106
unireader.lua
106
unireader.lua
@@ -34,9 +34,12 @@ UniReader = {
|
||||
-- gamma setting:
|
||||
globalgamma = 1.0, -- GAMMA_NO_GAMMA
|
||||
|
||||
-- size of current page for current zoom level in pixels
|
||||
-- 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,
|
||||
offset_x = 0,
|
||||
offset_y = 0,
|
||||
min_offset_x = 0,
|
||||
@@ -73,6 +76,7 @@ UniReader = {
|
||||
pagehash = nil,
|
||||
|
||||
jump_stack = {},
|
||||
highlight = {},
|
||||
toc = nil,
|
||||
|
||||
bbox = {}, -- override getUsedBBox
|
||||
@@ -85,14 +89,17 @@ function UniReader:new(o)
|
||||
return o
|
||||
end
|
||||
|
||||
--[[
|
||||
For a new specific reader,
|
||||
you must always overwrite following two methods:
|
||||
|
||||
* self:open()
|
||||
|
||||
overwrite other methods if needed.
|
||||
--]]
|
||||
----------------------------------------------------
|
||||
-- !!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
--
|
||||
-- For a new specific reader,
|
||||
-- you must always overwrite following two methods:
|
||||
--
|
||||
-- * self:open()
|
||||
-- * self:init()
|
||||
--
|
||||
-- overwrite other methods if needed.
|
||||
----------------------------------------------------
|
||||
function UniReader:init()
|
||||
end
|
||||
|
||||
@@ -102,6 +109,22 @@ function UniReader:open(filename, password)
|
||||
return false
|
||||
end
|
||||
|
||||
----------------------------------------------------
|
||||
-- You need to overwrite following two methods if your
|
||||
-- reader supports highlight feature.
|
||||
----------------------------------------------------
|
||||
|
||||
function UniReader:startHighLightMode()
|
||||
return
|
||||
end
|
||||
|
||||
function UniReader:highLightText()
|
||||
return
|
||||
end
|
||||
|
||||
function UniReader:toggleTextHighLight(word_list)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
--[ following are default methods ]--
|
||||
@@ -118,6 +141,9 @@ function UniReader:loadSettings(filename)
|
||||
local jumpstack = self.settings:readSetting("jumpstack")
|
||||
self.jump_stack = jumpstack or {}
|
||||
|
||||
local highlight = self.settings:readSetting("highlight")
|
||||
self.highlight = highlight or {}
|
||||
|
||||
local bbox = self.settings:readSetting("bbox")
|
||||
print("# bbox loaded "..dump(bbox))
|
||||
self.bbox = bbox
|
||||
@@ -186,7 +212,7 @@ function UniReader:drawOrCache(no, preCache)
|
||||
-- TODO: error handling
|
||||
return nil
|
||||
end
|
||||
local dc = self:setZoom(page)
|
||||
local dc = self:setzoom(page, preCache)
|
||||
|
||||
-- offset_x_in_page & offset_y_in_page is the offset within zoomed page
|
||||
-- they are always positive.
|
||||
@@ -291,7 +317,7 @@ function UniReader:clearCache()
|
||||
end
|
||||
|
||||
-- set viewer state according to zoom state
|
||||
function UniReader:setZoom(page)
|
||||
function UniReader:setzoom(page, preCache)
|
||||
local dc = DrawContext.new()
|
||||
local pwidth, pheight = page:getSize(self.nulldc)
|
||||
print("# page::getSize "..pwidth.."*"..pheight);
|
||||
@@ -421,6 +447,10 @@ function UniReader:setZoom(page)
|
||||
|
||||
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
|
||||
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
|
||||
@@ -473,6 +503,12 @@ function UniReader:show(no)
|
||||
"), src_off:("..offset_x..", "..offset_y.."), "..
|
||||
"width:"..width..", height:"..height)
|
||||
fb.bb:blitFrom(bb, dest_x, dest_y, offset_x, offset_y, width, height)
|
||||
|
||||
-- render highlights to page
|
||||
if self.highlight[no] then
|
||||
self:toggleTextHighLight(self.highlight[no])
|
||||
end
|
||||
|
||||
if self.rcount == self.rcountmax then
|
||||
print("full refresh")
|
||||
self.rcount = 1
|
||||
@@ -692,10 +728,10 @@ function UniReader:showTOC()
|
||||
local menu_items = {}
|
||||
local filtered_toc = {}
|
||||
-- build menu items
|
||||
for _k,_v in ipairs(self.toc) do
|
||||
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))
|
||||
table.insert(filtered_toc,v.page)
|
||||
end
|
||||
toc_menu = SelectMenu:new{
|
||||
menu_title = "Table of Contents",
|
||||
@@ -712,9 +748,9 @@ end
|
||||
|
||||
function UniReader:showJumpStack()
|
||||
local menu_items = {}
|
||||
for _k,_v in ipairs(self.jump_stack) do
|
||||
for k,v in ipairs(self.jump_stack) do
|
||||
table.insert(menu_items,
|
||||
_v.datetime.." -> Page ".._v.page.." ".._v.notes)
|
||||
v.datetime.." -> Page "..v.page.." "..v.notes)
|
||||
end
|
||||
jump_menu = SelectMenu:new{
|
||||
menu_title = "Jump Keeper (current page: "..self.pageno..")",
|
||||
@@ -730,6 +766,29 @@ function UniReader:showJumpStack()
|
||||
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
|
||||
toc_menu = SelectMenu:new{
|
||||
menu_title = "HighLights",
|
||||
item_array = menu_items,
|
||||
no_item_msg = "No HighLight found.",
|
||||
}
|
||||
item_no = toc_menu:choose(0, fb.bb:getHeight())
|
||||
if item_no then
|
||||
self:goto(highlight_dict[item_no].page)
|
||||
end
|
||||
end
|
||||
|
||||
function UniReader:showMenu()
|
||||
local ypos = height - 50
|
||||
local load_percent = (self.pageno / self.doc:getPages())
|
||||
@@ -810,6 +869,7 @@ function UniReader:inputLoop()
|
||||
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.settings:close()
|
||||
end
|
||||
|
||||
@@ -976,7 +1036,19 @@ function UniReader:addAllCommands()
|
||||
function(unireader)
|
||||
unireader:screenRotate("anticlockwise")
|
||||
end)
|
||||
self.commands:add(KEY_HOME,MOD_SHIFT_OR_ALT,"Home",
|
||||
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_HOME,nil,"Home",
|
||||
"exit application",
|
||||
function(unireader)
|
||||
keep_running = false
|
||||
|
||||
Reference in New Issue
Block a user