2017-09-10 12:26:49 +00:00
--[[--
2017-09-09 16:30:00 +00:00
ReaderLink is an abstraction for document - specific link interfaces .
2017-09-10 12:26:49 +00:00
] ]
2017-09-09 16:30:00 +00:00
2018-02-09 16:06:16 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2017-09-09 16:30:00 +00:00
local Device = require ( " device " )
local Event = require ( " ui/event " )
local Geom = require ( " ui/geometry " )
2014-01-15 15:36:47 +00:00
local GestureRange = require ( " ui/gesturerange " )
2018-07-11 16:05:30 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
2017-09-09 16:30:00 +00:00
local InputContainer = require ( " ui/widget/container/inputcontainer " )
2014-05-02 08:50:43 +00:00
local LinkBox = require ( " ui/widget/linkbox " )
2018-02-09 16:06:16 +00:00
local Notification = require ( " ui/widget/notification " )
2014-05-02 08:50:43 +00:00
local UIManager = require ( " ui/uimanager " )
2018-07-11 16:05:30 +00:00
local ffiutil = require ( " ffi/util " )
2017-01-15 20:55:06 +00:00
local logger = require ( " logger " )
2018-04-06 19:13:25 +00:00
local util = require ( " util " )
2014-08-17 12:29:38 +00:00
local _ = require ( " gettext " )
2017-09-09 16:30:00 +00:00
local Screen = Device.screen
2018-07-11 16:05:30 +00:00
local T = ffiutil.template
2014-01-15 15:36:47 +00:00
2014-01-18 07:04:24 +00:00
local ReaderLink = InputContainer : new {
2016-03-27 22:39:47 +00:00
location_stack = { }
2014-01-18 07:04:24 +00:00
}
2014-01-15 15:36:47 +00:00
function ReaderLink : init ( )
2014-03-13 13:52:43 +00:00
if Device : isTouchDevice ( ) then
self : initGesListener ( )
end
2014-10-09 09:41:23 +00:00
self.ui : registerPostInitCallback ( function ( )
self.ui . menu : registerToMainMenu ( self )
end )
2018-07-11 16:05:30 +00:00
-- For relative local file links
local directory , filename = util.splitFilePathName ( self.ui . document.file ) -- luacheck: no unused
self.document_dir = directory
2014-01-15 15:36:47 +00:00
end
2014-01-22 11:59:53 +00:00
function ReaderLink : onReadSettings ( config )
2014-03-13 13:52:43 +00:00
-- called when loading new document
2016-03-27 22:39:47 +00:00
self.location_stack = { }
2014-01-22 11:59:53 +00:00
end
2014-01-15 15:36:47 +00:00
function ReaderLink : initGesListener ( )
2014-03-13 13:52:43 +00:00
if Device : isTouchDevice ( ) then
self.ges_events = {
Tap = {
GestureRange : new {
ges = " tap " ,
range = Geom : new {
x = 0 , y = 0 ,
w = Screen : getWidth ( ) ,
h = Screen : getHeight ( )
}
}
} ,
Swipe = {
GestureRange : new {
ges = " swipe " ,
range = Geom : new {
x = 0 , y = 0 ,
w = Screen : getWidth ( ) ,
h = Screen : getHeight ( ) ,
}
}
} ,
}
end
2014-01-15 15:36:47 +00:00
end
2017-09-09 16:30:00 +00:00
local function isTapToFollowLinksOn ( )
return not G_reader_settings : isFalse ( " tap_to_follow_links " )
2015-04-01 10:11:14 +00:00
end
2016-12-03 13:02:04 +00:00
local function isSwipeToGoBackEnabled ( )
2015-10-03 16:20:41 +00:00
return G_reader_settings : readSetting ( " swipe_to_go_back " ) == true
2015-04-01 10:11:14 +00:00
end
2016-12-03 13:02:04 +00:00
local function isSwipeToFollowFirstLinkEnabled ( )
return G_reader_settings : readSetting ( " swipe_to_follow_first_link " ) == true
end
2017-09-11 20:56:15 +00:00
local function isSwipeToFollowNearestLinkEnabled ( )
return G_reader_settings : readSetting ( " swipe_to_follow_nearest_link " ) == true
end
local function isSwipeToJumpToLatestBookmarkEnabled ( )
return G_reader_settings : readSetting ( " swipe_to_jump_to_latest_bookmark " ) == true
end
2017-03-04 13:46:38 +00:00
function ReaderLink : addToMainMenu ( menu_items )
2014-08-17 12:29:38 +00:00
-- insert table to main reader menu
2017-03-04 13:46:38 +00:00
menu_items.follow_links = {
2017-09-09 16:30:00 +00:00
text = _ ( " Links " ) ,
2015-04-01 10:11:14 +00:00
sub_item_table = {
2017-09-09 16:30:00 +00:00
{
text = _ ( " Tap to follow links " ) ,
checked_func = isTapToFollowLinksOn ,
callback = function ( )
G_reader_settings : saveSetting ( " tap_to_follow_links " ,
not isTapToFollowLinksOn ( ) )
end ,
separator = true ,
2018-05-25 18:56:37 +00:00
help_text = _ ( [[Tap on links to follow them.]] ) ,
2015-04-01 10:11:14 +00:00
} ,
2017-09-09 16:30:00 +00:00
2015-04-01 10:11:14 +00:00
{
text = _ ( " Swipe to go back " ) ,
2016-12-03 13:02:04 +00:00
checked_func = isSwipeToGoBackEnabled ,
2015-04-01 10:11:14 +00:00
callback = function ( )
G_reader_settings : saveSetting ( " swipe_to_go_back " ,
2016-12-03 13:02:04 +00:00
not isSwipeToGoBackEnabled ( ) )
end ,
2018-05-25 18:56:37 +00:00
help_text = _ ( [[Swipe to the right to go back to the previous location after you have followed a link. When the location stack is empty, swiping to the right takes you to the previous page.]] ) ,
2016-12-03 13:02:04 +00:00
} ,
{
2017-09-11 20:56:15 +00:00
text = _ ( " Swipe to follow first link on page " ) ,
2016-12-03 13:02:04 +00:00
checked_func = isSwipeToFollowFirstLinkEnabled ,
callback = function ( )
G_reader_settings : saveSetting ( " swipe_to_follow_first_link " ,
not isSwipeToFollowFirstLinkEnabled ( ) )
2017-09-11 20:56:15 +00:00
if isSwipeToFollowFirstLinkEnabled ( ) then
G_reader_settings : delSetting ( " swipe_to_follow_nearest_link " ) -- can't have both
end
end ,
2018-05-25 18:56:37 +00:00
help_text = _ ( [[Swipe to the left to follow the first link in the current page.]] ) ,
2017-09-11 20:56:15 +00:00
} ,
{
text = _ ( " Swipe to follow nearest link " ) ,
checked_func = isSwipeToFollowNearestLinkEnabled ,
callback = function ( )
G_reader_settings : saveSetting ( " swipe_to_follow_nearest_link " ,
not isSwipeToFollowNearestLinkEnabled ( ) )
if isSwipeToFollowNearestLinkEnabled ( ) then
G_reader_settings : delSetting ( " swipe_to_follow_first_link " ) -- can't have both
end
end ,
2018-05-25 18:56:37 +00:00
help_text = _ ( [[Swipe to the left to follow the link nearest to where you started the swipe. This is useful when a small font is used and tapping on small links is tedious.]] ) ,
2017-09-11 20:56:15 +00:00
separator = true ,
} ,
{
text = _ ( " Swipe to jump to latest bookmark " ) ,
checked_func = isSwipeToJumpToLatestBookmarkEnabled ,
callback = function ( )
G_reader_settings : saveSetting ( " swipe_to_jump_to_latest_bookmark " ,
not isSwipeToJumpToLatestBookmarkEnabled ( ) )
2015-04-01 10:11:14 +00:00
end ,
2018-05-25 18:56:37 +00:00
help_text = _ ( [ [ Swipe to the left to go the most recently bookmarked page .
This can be useful to quickly swipe back and forth between what you are reading and some reference page ( for example notes , a map or a characters list ) .
If any of the other Swipe to follow link options is enabled , this will work only when the current page contains no link . ] ] ) ,
2015-04-01 10:11:14 +00:00
} ,
}
2017-02-28 21:46:32 +00:00
}
2018-01-17 19:28:33 +00:00
menu_items.go_to_previous_location = {
text = _ ( " Go back to previous location " ) ,
enabled_func = function ( ) return # self.location_stack > 0 end ,
callback = function ( ) self : onGoBackLink ( ) end ,
2018-09-04 21:55:58 +00:00
hold_callback = function ( touchmenu_instance )
UIManager : show ( ConfirmBox : new {
text = _ ( " Clear location history? " ) ,
ok_text = _ ( " Clear " ) ,
ok_callback = function ( )
self.location_stack = { }
touchmenu_instance : closeMenu ( )
end ,
} )
2018-02-09 16:06:16 +00:00
end ,
2018-01-17 19:28:33 +00:00
}
2014-08-17 12:29:38 +00:00
end
2018-02-09 16:06:16 +00:00
--- Check if a xpointer to <a> node really points to itself
function ReaderLink : isXpointerCoherent ( a_xpointer )
-- Get screen coordinates of xpointer
local doc_margins = self.ui . document._document : getPageMargins ( )
local doc_y , doc_x = self.ui . document : getPosFromXPointer ( a_xpointer )
local top_y = self.ui . document : getCurrentPos ( )
-- (strange, but using doc_margins.top is accurate even in scroll mode)
local screen_y = doc_y - top_y + doc_margins [ " top " ]
local screen_x = doc_x + doc_margins [ " left " ]
-- Get again link and a_xpointer from this position
local re_link_xpointer , re_a_xpointer = self.ui . document : getLinkFromPosition ( { x = screen_x , y = screen_y } ) -- luacheck: no unused
-- We should get the same a_xpointer. If not, crengine has messed up
-- and we should not trust this xpointer to get back to this link.
if re_a_xpointer ~= a_xpointer then
2018-07-07 18:22:44 +00:00
-- Try it again with screen_x+1 (in the rare cases where screen_x
-- fails, screen_x+1 usually works - probably something in crengine,
-- but easier to workaround here that way)
re_link_xpointer , re_a_xpointer = self.ui . document : getLinkFromPosition ( { x = screen_x + 1 , y = screen_y } ) -- luacheck: no unused
if re_a_xpointer ~= a_xpointer then
logger.info ( " not coherent a_xpointer: " , a_xpointer )
return false
end
2018-02-09 16:06:16 +00:00
end
return true
end
2017-09-09 16:30:00 +00:00
--- Gets link from gesture.
-- `Document:getLinkFromPosition()` behaves differently depending on
-- document type, so this function provides a wrapper.
function ReaderLink : getLinkFromGes ( ges )
2014-03-13 13:52:43 +00:00
if self.ui . document.info . has_pages then
local pos = self.view : screenToPageTransform ( ges.pos )
if pos then
2014-05-11 12:19:00 +00:00
-- link box in native page
2014-05-02 08:50:43 +00:00
local link , lbox = self.ui . document : getLinkFromPosition ( pos.page , pos )
if link and lbox then
2018-02-09 16:06:16 +00:00
return {
link = link ,
lbox = lbox ,
pos = pos ,
}
2014-03-13 13:52:43 +00:00
end
end
else
2018-02-09 16:06:16 +00:00
local link_xpointer , a_xpointer = self.ui . document : getLinkFromPosition ( ges.pos )
logger.dbg ( " getLinkFromPosition link_xpointer: " , link_xpointer )
logger.dbg ( " getLinkFromPosition a_xpointer: " , a_xpointer )
-- On some documents, crengine may sometimes give a wrong a_xpointer
-- (in some Wikipedia saved as EPUB, it would point to some other <A>
-- element in the same paragraph). If followed then back, we could get
-- to a different page. So, we check here how valid it is, and if not,
-- we just discard it so that addCurrentLocationToStack() is used.
if a_xpointer and not self : isXpointerCoherent ( a_xpointer ) then
a_xpointer = nil
end
if link_xpointer ~= " " then
-- This link's target xpointer is more precise than a classic
-- xpointer to top of a page: we can show a marker at its
-- y-position in target page
return {
xpointer = link_xpointer ,
marker_xpointer = link_xpointer ,
from_xpointer = a_xpointer ,
}
2017-09-09 16:30:00 +00:00
end
end
end
--- Highlights a linkbox if available and goes to it.
2018-02-09 16:06:16 +00:00
function ReaderLink : showLinkBox ( link )
if link and link.lbox then -- pdfdocument
2017-09-09 16:30:00 +00:00
-- screen box that holds the link
2018-02-09 16:06:16 +00:00
local sbox = self.view : pageToScreenTransform ( link.pos . page ,
self.ui . document : nativeToPageRectTransform ( link.pos . page , link.lbox ) )
2017-09-09 16:30:00 +00:00
if sbox then
UIManager : show ( LinkBox : new {
box = sbox ,
timeout = FOLLOW_LINK_TIMEOUT ,
2018-02-09 16:06:16 +00:00
callback = function ( ) self : onGotoLink ( link.link ) end
2017-09-09 16:30:00 +00:00
} )
return true
end
2018-02-09 16:06:16 +00:00
elseif link and link.xpointer ~= " " then -- credocument
return self : onGotoLink ( link )
2014-03-13 13:52:43 +00:00
end
2014-01-18 07:04:24 +00:00
end
2017-09-09 16:30:00 +00:00
function ReaderLink : onSetDimensions ( dimen )
-- update listening according to new screen dimen
if Device : isTouchDevice ( ) then
self : initGesListener ( )
end
end
function ReaderLink : onTap ( _ , ges )
if not isTapToFollowLinksOn ( ) then return end
2018-02-09 16:06:16 +00:00
local link = self : getLinkFromGes ( ges )
2017-09-09 16:30:00 +00:00
if link then
2018-02-09 16:06:16 +00:00
return self : showLinkBox ( link )
2017-09-09 16:30:00 +00:00
end
end
2018-01-31 12:18:37 +00:00
--- Remember current location so we can go back to it
function ReaderLink : addCurrentLocationToStack ( )
if self.ui . document.info . has_pages then
table.insert ( self.location_stack , self.ui . paging : getBookLocation ( ) )
else
2018-02-09 16:06:16 +00:00
table.insert ( self.location_stack , {
xpointer = self.ui . rolling : getBookLocation ( ) ,
} )
2018-01-31 12:18:37 +00:00
end
end
2017-09-09 16:30:00 +00:00
--- Goes to link.
2018-01-31 12:28:26 +00:00
function ReaderLink : onGotoLink ( link , neglect_current_location )
2017-01-15 20:55:06 +00:00
logger.dbg ( " onGotoLink: " , link )
2014-03-13 13:52:43 +00:00
if self.ui . document.info . has_pages then
2017-01-15 20:55:06 +00:00
-- internal pdf links have a "page" attribute, while external ones have an "uri" attribute
if link.page then -- Internal link
logger.dbg ( " Internal link: " , link )
2018-01-31 12:28:26 +00:00
if not neglect_current_location then
self : addCurrentLocationToStack ( )
end
2017-01-15 20:55:06 +00:00
self.ui : handleEvent ( Event : new ( " GotoPage " , link.page + 1 ) )
return true
end
link = link.uri -- external link
else
-- For crengine, internal links may look like :
-- #_doc_fragment_0_Organisation (link from anchor)
-- /body/DocFragment/body/ul[2]/li[5]/text()[3].16 (xpointer from full-text search)
-- If the XPointer does not exist (or is a full url), we will jump to page 1
-- Best to check that this link exists in document with the following,
-- which accepts both of the above legitimate xpointer as input.
2018-02-09 16:06:16 +00:00
if self.ui . document : isXPointerInDocument ( link.xpointer ) then
2017-01-15 20:55:06 +00:00
logger.dbg ( " Internal link: " , link )
2018-01-31 12:28:26 +00:00
if not neglect_current_location then
2018-02-09 16:06:16 +00:00
if link.from_xpointer then
-- We have a more precise xpointer than the xpointer to top of
-- current page that addCurrentLocationToStack() would give, and
-- we may be able to show a marker when back
local saved_location
if self.view . view_mode == " scroll " then
-- In scroll mode, we still use the top of page as the
-- xpointer to go back to, so we get back to the same view.
-- We can still show the marker at the link position
saved_location = {
xpointer = self.ui . rolling : getBookLocation ( ) ,
marker_xpointer = link.from_xpointer ,
}
else
-- In page mode, we use the same for go to and for marker,
-- as 'page mode' ensures we get back to the same view.
saved_location = {
xpointer = link.from_xpointer ,
marker_xpointer = link.from_xpointer ,
}
end
table.insert ( self.location_stack , saved_location )
else
self : addCurrentLocationToStack ( )
end
2018-01-31 12:28:26 +00:00
end
2018-02-09 16:06:16 +00:00
self.ui : handleEvent ( Event : new ( " GotoXPointer " , link.xpointer , link.marker_xpointer ) )
2017-01-15 20:55:06 +00:00
return true
end
end
logger.dbg ( " External link: " , link )
2018-07-11 16:05:30 +00:00
2017-01-15 20:55:06 +00:00
-- Check if it is a wikipedia link
2018-02-09 16:06:16 +00:00
local wiki_lang , wiki_page = link.xpointer : match ( [[https?://([^%.]+).wikipedia.org/wiki/([^/]+)]] )
2017-01-15 20:55:06 +00:00
if wiki_lang and wiki_page then
logger.dbg ( " Wikipedia link: " , wiki_lang , wiki_page )
-- Ask for user confirmation before launching lookup (on a
-- wikipedia page saved as epub, full of wikipedia links, it's
-- too easy to click on links when wanting to change page...)
2018-04-06 19:13:25 +00:00
-- But first check if this wikipedia article has been saved as EPUB
local epub_filename = util.replaceInvalidChars ( wiki_page : gsub ( " _ " , " " ) ) .. " . " .. string.upper ( wiki_lang ) .. " .epub "
local epub_fullpath
-- either in current book directory
local last_file = G_reader_settings : readSetting ( " lastfile " )
if last_file then
local current_book_dir = last_file : match ( " (.*)/ " )
local epub_path = current_book_dir .. " / " .. epub_filename
if util.pathExists ( epub_path ) then
epub_fullpath = epub_path
2017-01-15 20:55:06 +00:00
end
2018-04-06 19:13:25 +00:00
end
-- or in wikipedia save directory
if not epub_fullpath then
local dir = G_reader_settings : readSetting ( " wikipedia_save_dir " )
if not dir then dir = G_reader_settings : readSetting ( " home_dir " ) end
if not dir then dir = require ( " apps/filemanager/filemanagerutil " ) . getDefaultDir ( ) end
if dir then
local epub_path = dir .. " / " .. epub_filename
if util.pathExists ( epub_path ) then
epub_fullpath = epub_path
end
end
end
if epub_fullpath then
local MultiConfirmBox = require ( " ui/widget/multiconfirmbox " )
UIManager : show ( MultiConfirmBox : new {
text = T ( _ ( " Would you like to read this Wikipedia %1 article? \n \n %2 \n \n This article has previously been saved as EPUB. You may wish to read the saved EPUB instead. " ) , wiki_lang : upper ( ) , wiki_page : gsub ( " _ " , " " ) , epub_fullpath ) ,
choice1_text = _ ( " Read online " ) ,
choice1_callback = function ( )
UIManager : nextTick ( function ( )
self.ui : handleEvent ( Event : new ( " LookupWikipedia " , wiki_page , false , true , wiki_lang ) )
end )
end ,
choice2_text = _ ( " Read EPUB " ) ,
choice2_callback = function ( )
UIManager : scheduleIn ( 0.1 , function ( )
2018-07-11 16:05:30 +00:00
self.ui : switchDocument ( epub_fullpath )
2018-04-06 19:13:25 +00:00
end )
end ,
} )
else
UIManager : show ( ConfirmBox : new {
text = T ( _ ( " Would you like to read this Wikipedia %1 article? \n \n %2 \n " ) , wiki_lang : upper ( ) , wiki_page : gsub ( " _ " , " " ) ) ,
ok_callback = function ( )
UIManager : nextTick ( function ( )
self.ui : handleEvent ( Event : new ( " LookupWikipedia " , wiki_page , false , true , wiki_lang ) )
end )
end
} )
end
2018-07-11 16:05:30 +00:00
return true
end
-- Check if it is a link to a local file
local linked_filename = link.xpointer : gsub ( " ^file: " , " " ) -- remove local file protocol if any
local anchor
if linked_filename : find ( " ? " ) then -- remove any query string (including any following anchor)
linked_filename , anchor = linked_filename : match ( " ^(.-)(%?.*)$ " )
elseif linked_filename : find ( " # " ) then -- remove any anchor
linked_filename , anchor = linked_filename : match ( " ^(.-)(#.*)$ " )
2014-03-13 13:52:43 +00:00
end
2018-07-11 16:05:30 +00:00
linked_filename = ffiutil.joinPath ( self.document_dir , linked_filename ) -- get full path
linked_filename = ffiutil.realpath ( linked_filename ) -- clean full path from ./ or ../
if linked_filename and lfs.attributes ( linked_filename , " mode " ) == " file " then
local DocumentRegistry = require ( " document/documentregistry " )
local provider = DocumentRegistry : getProvider ( linked_filename )
if provider then
-- Display filename with anchor or query string, so the user gets
-- this information and can manually go to the appropriate place
local display_filename = linked_filename
if anchor then
display_filename = display_filename .. anchor
end
UIManager : show ( ConfirmBox : new {
text = T ( _ ( " Would you like to read this local document? \n \n %1 \n " ) , display_filename ) ,
ok_callback = function ( )
UIManager : scheduleIn ( 0.1 , function ( )
self.ui : switchDocument ( linked_filename )
end )
end
} )
else
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Link to unsupported local file: \n %1 " ) , link.xpointer ) ,
} )
end
return true
end
-- Not supported
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Invalid or external link: \n %1 " ) , link.xpointer ) ,
-- no timeout to allow user to type that link in his web browser
} )
2017-01-15 20:55:06 +00:00
-- don't propagate, user will notice and tap elsewhere if he wants to change page
2014-03-13 13:52:43 +00:00
return true
2014-01-18 07:04:24 +00:00
end
2017-09-09 16:30:00 +00:00
--- Goes back to previous location.
2015-04-01 10:11:14 +00:00
function ReaderLink : onGoBackLink ( )
2016-03-27 22:39:47 +00:00
local saved_location = table.remove ( self.location_stack )
if saved_location then
2018-02-09 16:06:16 +00:00
logger.dbg ( " GoBack: restoring: " , saved_location )
2016-03-27 22:39:47 +00:00
self.ui : handleEvent ( Event : new ( ' RestoreBookLocation ' , saved_location ) )
2016-08-12 01:23:23 +00:00
return true
2015-04-01 10:11:14 +00:00
end
end
2018-02-09 16:06:16 +00:00
function ReaderLink : onSwipe ( arg , ges )
2016-12-03 13:02:04 +00:00
if ges.direction == " east " then
if isSwipeToGoBackEnabled ( ) then
2018-02-09 16:06:16 +00:00
if # self.location_stack > 0 then
-- Remember if location stack is going to be empty, so we
-- can stop the propagation of next swipe back: so the user
-- knows it is empty and that next swipe back will get him
-- to previous page (and not to previous location)
self.swipe_back_resist = # self.location_stack == 1
return self : onGoBackLink ( )
elseif self.swipe_back_resist then
self.swipe_back_resist = false
-- Make that gesture don't do anything, and show a Notification
-- so the user knows why
UIManager : show ( Notification : new {
2018-02-10 10:42:50 +00:00
text = _ ( " Location history is empty " ) ,
2018-05-19 12:01:20 +00:00
timeout = 2 ,
2018-02-09 16:06:16 +00:00
} )
return true
end
2016-12-03 13:02:04 +00:00
end
elseif ges.direction == " west " then
2017-09-11 20:56:15 +00:00
local ret = false
2016-12-03 13:02:04 +00:00
if isSwipeToFollowFirstLinkEnabled ( ) then
2017-09-11 20:56:15 +00:00
ret = self : onGoToPageLink ( ges , true )
elseif isSwipeToFollowNearestLinkEnabled ( ) then
ret = self : onGoToPageLink ( ges )
end
-- If no link found, or no follow link option enabled,
-- jump to latest bookmark (if enabled)
if not ret and isSwipeToJumpToLatestBookmarkEnabled ( ) then
ret = self : onGoToLatestBookmark ( ges )
2016-12-03 13:02:04 +00:00
end
2017-09-11 20:56:15 +00:00
return ret
2016-12-03 13:02:04 +00:00
end
end
2017-09-11 20:56:15 +00:00
--- Goes to link nearest to the gesture (or first link in page)
function ReaderLink : onGoToPageLink ( ges , use_page_first_link )
local selected_link = nil
2016-12-03 13:02:04 +00:00
if self.ui . document.info . has_pages then
local pos = self.view : screenToPageTransform ( ges.pos )
if not pos then
return
end
local links = self.ui . document : getPageLinks ( pos.page )
2017-05-02 07:09:11 +00:00
if not links or # links == 0 then
2016-12-03 13:02:04 +00:00
return
end
-- DEBUG("PDF Page links : ", links)
-- We may get multiple links: internal ones (with "page" key)
-- that we're interested in, but also external links (no "page", but
-- a "uri" key) that we don't care about.
-- [2] = {
-- ["y1"] = 107.88977050781,
-- ["x1"] = 176.60360717773,
-- ["y0"] = 97.944396972656,
-- ["x0"] = 97,
-- ["page"] = 347
-- },
2018-02-09 16:06:16 +00:00
local pos_x , pos_y = pos.x , pos.y
2017-09-11 20:56:15 +00:00
local shortest_dist = nil
2016-12-03 13:02:04 +00:00
local first_y0 = nil
for _ , link in ipairs ( links ) do
if link [ " page " ] then
2017-09-11 20:56:15 +00:00
if use_page_first_link then
-- Links may not be in the order they are in the page, so let's
-- find the one with the smallest y0.
if first_y0 == nil or link [ " y0 " ] < first_y0 then
selected_link = link
first_y0 = link [ " y0 " ]
end
else
local start_dist = math.pow ( link.x0 - pos_x , 2 ) + math.pow ( link.y0 - pos_y , 2 )
local end_dist = math.pow ( link.x1 - pos_x , 2 ) + math.pow ( link.y1 - pos_y , 2 )
local min_dist = math.min ( start_dist , end_dist )
if shortest_dist == nil or min_dist < shortest_dist then
-- onGotoLink()'s GotoPage event needs the link
-- itself, and will use its "page" value
selected_link = link
shortest_dist = min_dist
end
2016-12-03 13:02:04 +00:00
end
end
end
else
local links = self.ui . document : getPageLinks ( )
2017-05-02 07:09:11 +00:00
if not links or # links == 0 then
2016-12-03 13:02:04 +00:00
return
end
-- DEBUG("CRE Page links : ", links)
-- We may get multiple links: internal ones (they have a "section" key)
-- that we're interested in, but also external links (no "section", but
-- a "uri" key) that we don't care about.
-- [1] = {
-- ["end_x"] = 825,
-- ["uri"] = "",
-- ["end_y"] = 333511,
-- ["start_x"] = 90,
-- ["start_y"] = 333511
-- },
-- [2] = {
-- ["end_x"] = 366,
-- ["section"] = "#_doc_fragment_19_ftn_fn6",
-- ["end_y"] = 1201,
-- ["start_x"] = 352,
-- ["start_y"] = 1201
2018-02-09 16:06:16 +00:00
-- ["a_xpointer"] = "/body/DocFragment/body/div/p[12]/sup[3]/a[3].0",
2016-12-03 13:02:04 +00:00
-- },
2018-02-09 16:06:16 +00:00
-- Note: with some documents and some links, crengine may give wrong
-- coordinates, and our code below may miss or give the wrong first
-- or nearest link...
2017-09-11 20:56:15 +00:00
local pos_x , pos_y = ges.pos . x , ges.pos . y
local shortest_dist = nil
2016-12-03 13:02:04 +00:00
local first_start_y = nil
for _ , link in ipairs ( links ) do
if link [ " section " ] then
2017-09-11 20:56:15 +00:00
if use_page_first_link then
-- links may not be in the order they are in the page, so let's
-- find the one with the smallest start_y.
if first_start_y == nil or link [ " start_y " ] < first_start_y then
2018-02-09 16:06:16 +00:00
selected_link = link
2017-09-11 20:56:15 +00:00
first_start_y = link [ " start_y " ]
end
else
local start_dist = math.pow ( link.start_x - pos_x , 2 ) + math.pow ( link.start_y - pos_y , 2 )
local end_dist = math.pow ( link.end_x - pos_x , 2 ) + math.pow ( link.end_y - pos_y , 2 )
local min_dist = math.min ( start_dist , end_dist )
if shortest_dist == nil or min_dist < shortest_dist then
2018-02-09 16:06:16 +00:00
selected_link = link
2017-09-11 20:56:15 +00:00
shortest_dist = min_dist
end
2016-12-03 13:02:04 +00:00
end
end
end
-- cre.cpp getPageLinks() does highlight found links :
-- sel.add( new ldomXRange(*links[i]) ); // highlight
-- and we'll find them highlighted when back from link.
-- So let's clear them now.
self.ui . document : clearSelection ( )
2018-02-09 16:06:16 +00:00
-- (Comment out previous line to visually see which links on the
-- page are not coherent: those not highlighted)
if selected_link then
logger.dbg ( " original selected_link " , selected_link )
-- Make it a link as expected by onGotoLink
selected_link = {
xpointer = selected_link [ " section " ] ,
marker_xpointer = selected_link [ " section " ] ,
from_xpointer = selected_link [ " a_xpointer " ] ,
}
logger.dbg ( " selected_link " , selected_link )
-- Check from_xpointer is coherent, and unset it if not
if selected_link.from_xpointer and not self : isXpointerCoherent ( selected_link.from_xpointer ) then
selected_link.from_xpointer = nil
end
end
2016-12-03 13:02:04 +00:00
end
2017-09-11 20:56:15 +00:00
if selected_link then
return self : onGotoLink ( selected_link )
end
end
function ReaderLink : onGoToLatestBookmark ( ges )
local latest_bookmark = self.ui . bookmark : getLatestBookmark ( )
if latest_bookmark then
if self.ui . document.info . has_pages then
-- self:onGotoLink() needs something with a page attribute.
-- we need to substract 1 to bookmark page, as links start from 0
-- and onGotoLink will add 1 - we need a fake_link (with a single
-- page attribute) so we don't touch the bookmark itself
local fake_link = { }
fake_link.page = latest_bookmark.page - 1
return self : onGotoLink ( fake_link )
else
2018-02-09 16:06:16 +00:00
-- Make it a link as expected by onGotoLink
local link
if latest_bookmark.pos0 then -- text highlighted, precise xpointer
link = {
xpointer = latest_bookmark.pos0 ,
marker_xpointer = latest_bookmark.pos0 ,
}
else -- page bookmarked, 'page' is a xpointer to top of page
link = {
xpointer = latest_bookmark.page ,
}
end
return self : onGotoLink ( link )
2017-09-11 20:56:15 +00:00
end
2014-03-13 13:52:43 +00:00
end
2014-01-15 15:36:47 +00:00
end
return ReaderLink