summaryrefslogtreecommitdiff
path: root/tex/context/base/mkxl/strc-not.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkxl/strc-not.lmt')
-rw-r--r--tex/context/base/mkxl/strc-not.lmt530
1 files changed, 530 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/strc-not.lmt b/tex/context/base/mkxl/strc-not.lmt
new file mode 100644
index 000000000..df3b00d78
--- /dev/null
+++ b/tex/context/base/mkxl/strc-not.lmt
@@ -0,0 +1,530 @@
+if not modules then modules = { } end modules ['strc-not'] = {
+ version = 1.001,
+ comment = "companion to strc-not.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format = string.format
+local next = next
+
+local trace_notes = false trackers.register("structures.notes", function(v) trace_notes = v end)
+local trace_references = false trackers.register("structures.notes.references", function(v) trace_references = v end)
+
+local report_notes = logs.reporter("structure","notes")
+
+local structures = structures
+local helpers = structures.helpers
+local lists = structures.lists
+local sections = structures.sections
+local counters = structures.counters
+local notes = structures.notes
+local references = structures.references
+local counterspecials = counters.specials
+
+local texgetcount = tex.getcount
+local texgetbox = tex.getbox
+
+-- todo: allocate
+
+notes.states = notes.states or { }
+lists.enhancers = lists.enhancers or { }
+notes.numbers = notes.numbers or { }
+
+storage.register("structures/notes/states", notes.states, "structures.notes.states")
+storage.register("structures/notes/numbers", notes.numbers, "structures.notes.numbers")
+
+local notestates = notes.states
+local notedata = table.setmetatableindex("table")
+
+local variables = interfaces.variables
+local context = context
+local commands = commands
+
+local implement = interfaces.implement
+
+-- state: store, insert, postpone
+
+local function store(tag,n)
+ -- somewhat weird but this is a cheap hook spot
+ if not counterspecials[tag] then
+ counterspecials[tag] = function(tag)
+ context.doresetlinenotecompression(tag) -- maybe flag that controls it
+ end
+ end
+ --
+ local nd = notedata[tag]
+ local nnd = #nd + 1
+ nd[nnd] = n
+ local state = notestates[tag]
+ if not state then
+ report_notes("unknown state for %a",tag)
+ elseif state.kind ~= "insert" then
+ if trace_notes then
+ report_notes("storing %a with state %a as %a",tag,state.kind,nnd)
+ end
+ state.start = state.start or nnd
+ end
+ return nnd
+end
+
+notes.store = store
+
+implement {
+ name = "storenote",
+ actions = { store, context },
+ arguments = { "string", "integer" }
+}
+
+local function get(tag,n) -- tricky ... only works when defined
+ local nd = notedata[tag]
+ if not n then
+ n = #nd
+ end
+ nd = nd[n]
+ if nd then
+ if trace_notes then
+ report_notes("getting note %a of %a with listindex %a",n,tag,nd)
+ end
+ -- is this right?
+ local newdata = lists.cached[nd]
+ return newdata
+ end
+end
+
+local function getn(tag)
+ return #notedata[tag]
+end
+
+notes.get = get
+notes.getn = getn
+
+-- we could make a special enhancer
+
+local function listindex(tag,n)
+ local ndt = notedata[tag]
+ return ndt and ndt[n]
+end
+
+notes.listindex = listindex
+
+implement {
+ name = "notelistindex",
+ actions = { listindex, context },
+ arguments = { "string", "integer" }
+}
+
+local function setstate(tag,newkind)
+ local state = notestates[tag]
+ if trace_notes then
+ report_notes("setting state of %a from %s to %s",tag,(state and state.kind) or "unset",newkind)
+ end
+ if not state then
+ state = {
+ kind = newkind
+ }
+ notestates[tag] = state
+ elseif newkind == "insert" then
+ if not state.start then
+ state.kind = newkind
+ end
+ else
+-- if newkind == "postpone" and state.kind == "store" then
+-- else
+ state.kind = newkind
+-- end
+ end
+ -- state.start can already be set and will be set when an entry is added or flushed
+ return state
+end
+
+local function getstate(tag)
+ local state = notestates[tag]
+ return state and state.kind or "unknown"
+end
+
+notes.setstate = setstate
+notes.getstate = getstate
+
+
+
+implement {
+ name = "setnotestate",
+ actions = setstate,
+ arguments = "2 strings",
+}
+
+implement {
+ name = "getnotestate",
+ actions = { getstate, context },
+ arguments = "string"
+}
+
+function notes.define(tag,kind,number)
+ local state = setstate(tag,kind)
+ notes.numbers[number] = state
+ state.number = number
+end
+
+implement {
+ name = "definenote",
+ actions = notes.define,
+ arguments = { "string", "string", "integer" }
+}
+
+function notes.save(tag,newkind)
+ local state = notestates[tag]
+ if state and not state.saved then
+ if trace_notes then
+ report_notes("saving state of %a, old: %a, new %a",tag,state.kind,newkind or state.kind)
+ end
+ state.saveddata = notedata[tag]
+ state.savedkind = state.kind
+ state.kind = newkind or state.kind
+ state.saved = true
+ notedata[tag] = { }
+ end
+end
+
+function notes.restore(tag,forcedstate)
+ local state = notestates[tag]
+ if state and state.saved then
+ if trace_notes then
+ report_notes("restoring state of %a, old: %a, new: %a",tag,state.kind,state.savedkind)
+ end
+ notedata[tag] = state.saveddata
+ state.kind = forcedstate or state.savedkind
+ state.saveddata = nil
+ state.saved = false
+ end
+end
+
+implement { name = "savenote", actions = notes.save, arguments = "2 strings" }
+implement { name = "restorenote", actions = notes.restore, arguments = "2 strings" }
+
+local function hascontent(tag)
+ local ok = notestates[tag]
+ if ok then
+ if ok.kind == "insert" then
+ ok = texgetbox(ok.number)
+ if ok then
+ ok = tbs.list
+ ok = lst and lst.next
+ end
+ else
+ ok = ok.start
+ end
+ end
+ return ok and true or false
+end
+
+notes.hascontent = hascontent
+
+implement {
+ name = "doifnotecontent",
+ actions = { hascontent, commands.doif },
+ arguments = "string",
+}
+
+local function internal(tag,n)
+ local nd = get(tag,n)
+ if nd then
+ local r = nd.references
+ if r then
+ local i = r.internal
+ return i and references.internals[i] -- dependency on references
+ end
+ end
+ return nil
+end
+
+local function ordered(kind,name,n)
+ local o = lists.ordered[kind]
+ o = o and o[name]
+ return o and o[n]
+end
+
+notes.internal = internal
+notes.ordered = ordered
+
+-- local function onsamepageasprevious(tag)
+-- local same = false
+-- local n = getn(tag,n)
+-- local current = get(tag,n)
+-- local previous = get(tag,n-1)
+-- if current and previous then
+-- local cr = current.references
+-- local pr = previous.references
+-- same = cr and pr and cr.realpage == pr.realpage
+-- end
+-- return same and true or false
+-- end
+
+local function onsamepageasprevious(tag)
+ local n = getn(tag,n)
+ local current = get(tag,n)
+ if not current then
+ return false
+ end
+ local cr = current.references
+ if not cr then
+ return false
+ end
+ local previous = get(tag,n-1)
+ if not previous then
+ return false
+ end
+ local pr = previous.references
+ if not pr then
+ return false
+ end
+ return cr.realpage == pr.realpage
+end
+
+notes.doifonsamepageasprevious = onsamepageasprevious
+
+implement {
+ name = "doifnoteonsamepageasprevious",
+ actions = { onsamepageasprevious, commands.doifelse },
+ arguments = "string",
+}
+
+function notes.checkpagechange(tag) -- called before increment !
+ local nd = notedata[tag] -- can be unset at first entry
+ if nd then
+ local current = ordered("note",tag,#nd)
+ local nextone = ordered("note",tag,#nd+1)
+ if nextone then
+ -- we can use data from the previous pass
+ if nextone.pagenumber.number > current.pagenumber.number then
+ counters.reset(tag)
+ end
+ elseif current then
+ -- we need to locate the next one, best guess
+ if texgetcount("realpageno") > current.pagenumber.number then
+ counters.reset(tag)
+ end
+ end
+ end
+end
+
+function notes.postpone()
+ if trace_notes then
+ report_notes("postponing all insert notes")
+ end
+ for tag, state in next, notestates do
+ if state.kind ~= "store" then
+ setstate(tag,"postpone")
+ end
+ end
+end
+
+implement {
+ name = "postponenotes",
+ actions = notes.postpone
+}
+
+local function getinternal(tag,n)
+ local li = internal(tag,n)
+ if li then
+ local references = li.references
+ if references then
+ return references.internal or 0
+ end
+ end
+ return 0
+end
+
+local function getdeltapage(tag,n)
+ -- 0:unknown 1:textbefore, 2:textafter, 3:samepage
+ local li = internal(tag,n)
+ if li then
+ local references = li.references
+ if references then
+ -- local symb = structures.references.collected[""]["symb:"..tag..":"..n]
+ local rymb = structures.references.collected[""]
+ local symb = rymb and rymb["*"..(references.internal or 0)]
+ local notepage = references.realpage or 0
+ local symbolpage = symb and symb.references.realpage or -1
+ if trace_references then
+ report_notes("note number %a of %a points from page %a to page %a",n,tag,symbolpage,notepage)
+ end
+ if notepage < symbolpage then
+ return 3 -- after
+ elseif notepage > symbolpage then
+ return 2 -- before
+ elseif notepage > 0 then
+ return 1 -- same
+ end
+ else
+ -- might be a note that is not flushed due to to deep
+ -- nesting in a vbox
+ end
+ end
+ return 0
+end
+
+notes.getinternal = getinternal
+notes.getdeltapage = getdeltapage
+
+implement { name = "noteinternal", actions = { getinternal, context }, arguments = { "string", "integer" } }
+implement { name = "notedeltapage", actions = { getdeltapage, context }, arguments = { "string", "integer" } }
+
+local function flushnotes(tag,whatkind,how) -- store and postpone
+ local state = notestates[tag]
+ local kind = state.kind
+ if kind == whatkind then
+ local nd = notedata[tag]
+ local ns = state.start -- first index
+ if kind == "postpone" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ for i=ns,#nd do
+ context.handlenoteinsert(tag,i)
+ end
+ end
+ state.start = nil
+ state.kind = "insert"
+ elseif kind == "store" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ -- todo: as registers: start, stop, inbetween
+ for i=ns,#nd do
+ -- tricky : trialtypesetting
+ if how == variables.page then
+ local rp = get(tag,i)
+ rp = rp and rp.references
+ rp = rp and rp.symbolpage or 0
+ if rp > texgetcount("realpageno") then
+ state.start = i
+ return
+ end
+ end
+ if i > ns then
+ context.betweennoteitself(tag)
+ end
+ context.handlenoteitself(tag,i)
+ end
+ end
+ state.start = nil
+ elseif kind == "reset" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ end
+ state.start = nil
+ elseif trace_notes then
+ report_notes("not flushing state %a of %a",whatkind,tag)
+ end
+ elseif trace_notes then
+ report_notes("not flushing state %a of %a",whatkind,tag)
+ end
+end
+
+local function flushpostponednotes()
+ if trace_notes then
+ report_notes("flushing all postponed notes")
+ end
+ for tag, _ in next, notestates do
+ flushnotes(tag,"postpone")
+ end
+end
+
+implement {
+ name = "flushpostponednotes",
+ actions = flushpostponednotes
+}
+
+implement {
+ name = "flushnotes",
+ actions = flushnotes,
+ arguments = "3 strings",
+}
+
+function notes.resetpostponed()
+ if trace_notes then
+ report_notes("resetting all postponed notes")
+ end
+ for tag, state in next, notestates do
+ if state.kind == "postpone" then
+ state.start = nil
+ state.kind = "insert"
+ end
+ end
+end
+
+implement {
+ name = "notetitle",
+ actions = function(tag,n) lists.savedlisttitle(tag,notedata[tag][n]) end,
+ arguments = { "string", "integer" }
+}
+
+implement {
+ name = "noteprefixednumber",
+ actions = function(tag,n) lists.savedlistprefixednumber(tag,notedata[tag][n]) end,
+ arguments = { "string", "integer" }
+}
+
+function notes.internalid(tag,n)
+ local nd = get(tag,n)
+ if nd then
+ local r = nd.references
+ return r.internal
+ end
+end
+
+-- for the moment here but better in some builder modules
+
+-- gets register "n" and location "i" (where 1 is before)
+
+-- this is an experiment, we will make a more general handler instead
+-- of the current note one
+
+local report_insert = logs.reporter("pagebuilder","insert")
+local trace_insert = false trackers.register("pagebuilder.insert",function(v) trace_insert = v end)
+
+local texgetglue = tex.getglue
+----- texsetglue = tex.setglue
+
+local tonode = nodes.nuts.tonode
+local newgluespec = nodes.nuts.pool.gluespec -- nodes.pool.gluespec
+
+-- needs to be sorted out!
+
+local function check_spacing(index,slot)
+ -- we can also check for tex.insertheights > 0 (instead of i > 1)
+ local gn, pn, mn = tex.getinsertdistance(index) -- ,true so no testing needed
+ if not pn then pn = 0 end
+ if not mn then mn = 0 end
+ local gi, pi, mi = texgetglue(slot > 1 and "s_strc_notes_inbetween" or "s_strc_notes_before")
+ if not pi then pi = 0 end
+ if not mi then mi = 0 end
+ local gt = gn + gi
+ local pt = pn + pi
+ local mt = mn + mi
+ if trace_insert then
+ report_insert("%s %i: %p plus %p minus %p","always ",n,gn,pn,mn)
+ report_insert("%s %i: %p plus %p minus %p",i > 1 and "inbetween" or "before ",n,gi,pi,mi)
+ report_insert("%s %i: %p plus %p minus %p","effective",n,gt,pt,mt)
+ end
+ return gt, pt, mt
+end
+
+notes.check_spacing = check_spacing
+
+-- only notes, kind of hardcoded .. bah
+
+callback.register("build_page_insert", function(index,slot)
+ local state = notes.numbers[index]
+ if state then
+ return tonode(newgluespec(check_spacing(index,slot)))
+ else
+ return tonode(newgluespec())
+ end
+end)