if not modules then modules = { } end modules ['strc-bkm'] = { version = 0.200, comment = "companion to strc-bkm.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- Future version will support adding arbitrary bookmarks with -- associated complex actions (rather trivial to implement). -- this should become proper separated backend code -- we should hook the placement into everystoptext ... needs checking local format, concat, gsub = string.format, table.concat, string.gsub local utfvalues = utf.values local settings_to_hash = utilities.parsers.settings_to_hash local codeinjections = backends.codeinjections local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end) local report_bookmarks = logs.reporter("structure","bookmarks") local structures = structures structures.bookmarks = structures.bookmarks or { } local bookmarks = structures.bookmarks local sections = structures.sections local lists = structures.lists local levelmap = sections.levelmap local variables = interfaces.variables bookmarks.method = "internal" -- or "page" local names, opened, forced, numbered = { }, { }, { }, { } function bookmarks.register(settings) local force = settings.force == variables.yes local number = settings.number == variables.yes local allopen = settings.opened == variables.all for k, v in next, settings_to_hash(settings.names or "") do names[k] = true if force then forced[k] = true if allopen then opened[k] = true end end if number then numbered[k] = true end end if not allopen then for k, v in next, settings_to_hash(settings.opened or "") do opened[k] = true end end end function bookmarks.overload(name,text) local l, ls = lists.tobesaved, nil if #l == 0 then -- no entries elseif name == "" then ls = l[#l] else for i=#l,0,-1 do local li = l[i] local metadata = li.metadata if metadata and not metadata.nolist and metadata.name == name then ls = li break end end end if ls then ls.titledata.bookmark = text end end local function stripped(str) -- kind of generic str = gsub(str,"\\([A-Z]+)","%1") -- \LOGO str = gsub(str,"\\ "," ") -- \ str = gsub(str,"\\([A-Za-z]+) *{(.-)}","%2") -- \bla{...} str = gsub(str," +"," ") -- spaces return str end -- todo: collect specs and collect later i.e. multiple places local numberspec = { } function bookmarks.setup(spec) -- table.merge(numberspec,spec) for k, v in next, spec do numberspec[k] = v end end -- function bookmarks.place() -- if next(names) then -- local list = lists.filtercollected(names,"all",nil,lists.collected,forced) -- if #list > 0 then -- local levels, noflevels, lastlevel = { }, 0, 1 -- for i=1,#list do -- local li = list[i] -- local metadata = li.metadata -- local name = metadata.name -- if not metadata.nolist or forced[name] then -- and levelmap[name] then -- local titledata = li.titledata -- if titledata then -- local structural = levelmap[name] -- lastlevel = structural or lastlevel -- local title = titledata.bookmark -- if not title or title == "" then -- -- We could typeset the title and then convert it. -- if not structural then -- -- placeholder, todo: bookmarklabel -- title = name .. ": " .. (titledata.title or "?") -- else -- title = titledata.title or "?" -- end -- end -- if numbered[name] then -- local sectiondata = sections.collected[li.references.section] -- local numberdata = li.numberdata -- if sectiondata and numberdata and not numberdata.hidenumber then -- -- we could typeset the number and convert it -- title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title -- end -- end -- noflevels = noflevels + 1 -- levels[noflevels] = { -- lastlevel, -- stripped(title), -- can be replaced by converter -- li.references, -- has internal and realpage -- allopen or opened[name] -- } -- end -- end -- end -- bookmarks.finalize(levels) -- end -- function bookmarks.place() end -- prevent second run -- end -- end function bookmarks.place() if next(names) then local levels = { } local noflevels = 0 local lastlevel = 1 local nofblocks = #lists.sectionblocks -- always >= 1 local showblocktitle = toboolean(numberspec.showblocktitle,true) for i=1,nofblocks do local block = lists.sectionblocks[i] local blockdone = nofblocks == 1 local list = lists.filtercollected(names,block..":all",nil,lists.collected,forced) for i=1,#list do local li = list[i] local metadata = li.metadata local name = metadata.name if not metadata.nolist or forced[name] then -- and levelmap[name] then local titledata = li.titledata if titledata then if not blockdone then if showblocktitle then -- add block entry local blockdata = sections.sectionblockdata[block] noflevels = noflevels + 1 levels[noflevels] = { 1, -- toplevel stripped(blockdata.bookmark ~= "" and blockdata.bookmark or block), li.references, allopen or opened[name] -- same as first entry } end blockdone = true end local structural = levelmap[name] lastlevel = structural or lastlevel if nofblocks > 1 then -- we have a block so increase the level lastlevel = lastlevel + 1 end local title = titledata.bookmark if not title or title == "" then -- We could typeset the title and then convert it. if not structural then -- placeholder, todo: bookmarklabel title = name .. ": " .. (titledata.title or "?") else title = titledata.title or "?" end end if numbered[name] then local sectiondata = sections.collected[li.references.section] local numberdata = li.numberdata if sectiondata and numberdata and not numberdata.hidenumber then -- we could typeset the number and convert it title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title end end noflevels = noflevels + 1 levels[noflevels] = { lastlevel, stripped(title), -- can be replaced by converter li.references, -- has internal and realpage allopen or opened[name] } end end end end bookmarks.finalize(levels) function bookmarks.place() end -- prevent second run end end function bookmarks.flatten(levels) -- This function promotes leading structurelements with a higher level -- to the next lower level. Such situations are the result of lack of -- structure: a subject preceding a chapter in a sectionblock. So, the -- following code runs over section blocks as well. (bookmarks-007.tex) local noflevels = #levels if noflevels > 1 then local skip, start, one = false, 1, levels[1] local first, block = one[1], one[3].block for i=2,noflevels do local li = levels[i] local new, newblock = li[1], li[3].block if newblock ~= block then first, block, start, skip = new, newblock, i, false elseif skip then -- go on elseif new > first then skip = true elseif new < first then for j=start,i-1 do local lj = levels[j] local old = lj[1] lj[1] = new if trace_bookmarks then report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,lj[2]) end end skip = true end end end end function bookmarks.finalize(levels) -- This function can be overloaded by an optional converter -- that uses nodes.toutf on a typeset stream. This is something -- that we will support when the main loop has become a coroutine. codeinjections.addbookmarks(levels,bookmarks.method) end -- interface commands.overloadbookmark = bookmarks.overload commands.registerbookmark = bookmarks.register commands.setupbookmarks = bookmarks.setup