1if not modules then modules = { } end modules ['strc-not'] = {
2 version = 1.001,
3 comment = "companion to strc-not.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local format = string.format
10local next = next
11
12local trace_notes = false trackers.register("structures.notes", function(v) trace_notes = v end)
13local trace_references = false trackers.register("structures.notes.references", function(v) trace_references = v end)
14
15local report_notes = logs.reporter("structure","notes")
16
17local structures = structures
18local helpers = structures.helpers
19local lists = structures.lists
20local sections = structures.sections
21local counters = structures.counters
22local notes = structures.notes
23local references = structures.references
24local counterspecials = counters.specials
25
26local texgetcount = tex.getcount
27local texgetbox = tex.getbox
28
29
30
31notes.states = notes.states or { }
32lists.enhancers = lists.enhancers or { }
33notes.numbers = notes.numbers or { }
34
35storage.register("structures/notes/states", notes.states, "structures.notes.states")
36storage.register("structures/notes/numbers", notes.numbers, "structures.notes.numbers")
37
38local notestates = notes.states
39local notedata = table.setmetatableindex("table")
40
41local variables = interfaces.variables
42local context = context
43local commands = commands
44
45local implement = interfaces.implement
46
47
48
49local function store(tag,n)
50
51 if not counterspecials[tag] then
52 counterspecials[tag] = function(tag)
53 context.doresetlinenotecompression(tag)
54 end
55 end
56
57 local nd = notedata[tag]
58 local nnd = #nd + 1
59 nd[nnd] = n
60 local state = notestates[tag]
61 if not state then
62 report_notes("unknown state for %a",tag)
63 elseif state.kind ~= "insert" then
64 if trace_notes then
65 report_notes("storing %a with state %a as %a",tag,state.kind,nnd)
66 end
67 state.start = state.start or nnd
68 end
69 return nnd
70end
71
72notes.store = store
73
74implement {
75 name = "storenote",
76 actions = { store, context },
77 arguments = { "string", "integer" }
78}
79
80local function get(tag,n)
81 local nd = notedata[tag]
82 if not n then
83 n = #nd
84 end
85 nd = nd[n]
86 if nd then
87 if trace_notes then
88 report_notes("getting note %a of %a with listindex %a",n,tag,nd)
89 end
90
91 local newdata = lists.cached[nd]
92 return newdata
93 end
94end
95
96local function getn(tag)
97 return #notedata[tag]
98end
99
100notes.get = get
101notes.getn = getn
102
103
104
105local function listindex(tag,n)
106 local ndt = notedata[tag]
107 return ndt and ndt[n]
108end
109
110notes.listindex = listindex
111
112implement {
113 name = "notelistindex",
114 actions = { listindex, context },
115 arguments = { "string", "integer" }
116}
117
118local function setstate(tag,newkind)
119 local state = notestates[tag]
120 if trace_notes then
121 report_notes("setting state of %a from %s to %s",tag,(state and state.kind) or "unset",newkind)
122 end
123 if not state then
124 state = {
125 kind = newkind
126 }
127 notestates[tag] = state
128 elseif newkind == "insert" then
129 if not state.start then
130 state.kind = newkind
131 end
132 else
133
134
135 state.kind = newkind
136
137 end
138
139 return state
140end
141
142local function getstate(tag)
143 local state = notestates[tag]
144 return state and state.kind or "unknown"
145end
146
147notes.setstate = setstate
148notes.getstate = getstate
149
150
151
152implement {
153 name = "setnotestate",
154 actions = setstate,
155 arguments = "2 strings",
156}
157
158implement {
159 name = "getnotestate",
160 actions = { getstate, context },
161 arguments = "string"
162}
163
164function notes.define(tag,kind,number)
165 local state = setstate(tag,kind)
166 notes.numbers[number] = state
167 state.number = number
168end
169
170implement {
171 name = "definenote",
172 actions = notes.define,
173 arguments = { "string", "string", "integer" }
174}
175
176function notes.save(tag,newkind)
177 local state = notestates[tag]
178 if state and not state.saved then
179 if trace_notes then
180 report_notes("saving state of %a, old: %a, new %a",tag,state.kind,newkind or state.kind)
181 end
182 state.saveddata = notedata[tag]
183 state.savedkind = state.kind
184 state.kind = newkind or state.kind
185 state.saved = true
186 notedata[tag] = { }
187 end
188end
189
190function notes.restore(tag,forcedstate)
191 local state = notestates[tag]
192 if state and state.saved then
193 if trace_notes then
194 report_notes("restoring state of %a, old: %a, new: %a",tag,state.kind,state.savedkind)
195 end
196 notedata[tag] = state.saveddata
197 state.kind = forcedstate or state.savedkind
198 state.saveddata = nil
199 state.saved = false
200 end
201end
202
203implement { name = "savenote", actions = notes.save, arguments = "2 strings" }
204implement { name = "restorenote", actions = notes.restore, arguments = "2 strings" }
205
206local function hascontent(tag)
207 local ok = notestates[tag]
208 if ok then
209 if ok.kind == "insert" then
210 ok = texgetbox(ok.number)
211 if ok then
212 ok = tbs.list
213 ok = lst and lst.next
214 end
215 else
216 ok = ok.start
217 end
218 end
219 return ok and true or false
220end
221
222notes.hascontent = hascontent
223
224implement {
225 name = "doifnotecontent",
226 actions = { hascontent, commands.doif },
227 arguments = "string",
228}
229
230local function internal(tag,n)
231 local nd = get(tag,n)
232 if nd then
233 local r = nd.references
234 if r then
235 local i = r.internal
236 return i and references.internals[i]
237 end
238 end
239 return nil
240end
241
242local function ordered(kind,name,n)
243 local o = lists.ordered[kind]
244 o = o and o[name]
245 return o and o[n]
246end
247
248notes.internal = internal
249notes.ordered = ordered
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264local function onsamepageasprevious(tag)
265 local n = getn(tag,n)
266 local current = get(tag,n)
267 if not current then
268 return false
269 end
270 local cr = current.references
271 if not cr then
272 return false
273 end
274 local previous = get(tag,n-1)
275 if not previous then
276 return false
277 end
278 local pr = previous.references
279 if not pr then
280 return false
281 end
282 return cr.realpage == pr.realpage
283end
284
285notes.doifonsamepageasprevious = onsamepageasprevious
286
287implement {
288 name = "doifnoteonsamepageasprevious",
289 actions = { onsamepageasprevious, commands.doifelse },
290 arguments = "string",
291}
292
293function notes.checkpagechange(tag)
294 local nd = notedata[tag]
295 if nd then
296 local current = ordered("note",tag,#nd)
297 local nextone = ordered("note",tag,#nd+1)
298 if nextone then
299
300 if nextone.pagenumber.number > current.pagenumber.number then
301 counters.reset(tag)
302 end
303 elseif current then
304
305 if texgetcount("realpageno") > current.pagenumber.number then
306 counters.reset(tag)
307 end
308 end
309 end
310end
311
312function notes.postpone()
313 if trace_notes then
314 report_notes("postponing all insert notes")
315 end
316 for tag, state in next, notestates do
317 if state.kind ~= "store" then
318 setstate(tag,"postpone")
319 end
320 end
321end
322
323implement {
324 name = "postponenotes",
325 actions = notes.postpone
326}
327
328local function getinternal(tag,n)
329 local li = internal(tag,n)
330 if li then
331 local references = li.references
332 if references then
333 return references.internal or 0
334 end
335 end
336 return 0
337end
338
339local function getdeltapage(tag,n)
340
341 local li = internal(tag,n)
342 if li then
343 local references = li.references
344 if references then
345
346 local rymb = structures.references.collected[""]
347 local symb = rymb and rymb["*"..(references.internal or 0)]
348 local notepage = references.realpage or 0
349 local symbolpage = symb and symb.references.realpage or -1
350 if trace_references then
351 report_notes("note number %a of %a points from page %a to page %a",n,tag,symbolpage,notepage)
352 end
353 if notepage < symbolpage then
354 return 3
355 elseif notepage > symbolpage then
356 return 2
357 elseif notepage > 0 then
358 return 1
359 end
360 else
361
362
363 end
364 end
365 return 0
366end
367
368notes.getinternal = getinternal
369notes.getdeltapage = getdeltapage
370
371implement { name = "noteinternal", actions = { getinternal, context }, arguments = { "string", "integer" } }
372implement { name = "notedeltapage", actions = { getdeltapage, context }, arguments = { "string", "integer" } }
373
374local function flushnotes(tag,whatkind,how)
375 local state = notestates[tag]
376 local kind = state.kind
377 if kind == whatkind then
378 local nd = notedata[tag]
379 local ns = state.start
380 if kind == "postpone" then
381 if nd and ns then
382 if trace_notes then
383 report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
384 end
385 for i=ns,#nd do
386 context.handlenoteinsert(tag,i)
387 end
388 end
389 state.start = nil
390 state.kind = "insert"
391 elseif kind == "store" then
392 if nd and ns then
393 if trace_notes then
394 report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
395 end
396
397 for i=ns,#nd do
398
399 if how == variables.page then
400 local rp = get(tag,i)
401 rp = rp and rp.references
402 rp = rp and rp.symbolpage or 0
403 if rp > texgetcount("realpageno") then
404 state.start = i
405 return
406 end
407 end
408 if i > ns then
409 context.betweennoteitself(tag)
410 end
411 context.handlenoteitself(tag,i)
412 end
413 end
414 state.start = nil
415 elseif kind == "reset" then
416 if nd and ns then
417 if trace_notes then
418 report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
419 end
420 end
421 state.start = nil
422 elseif trace_notes then
423 report_notes("not flushing state %a of %a",whatkind,tag)
424 end
425 elseif trace_notes then
426 report_notes("not flushing state %a of %a",whatkind,tag)
427 end
428end
429
430local function flushpostponednotes()
431 if trace_notes then
432 report_notes("flushing all postponed notes")
433 end
434 for tag, _ in next, notestates do
435 flushnotes(tag,"postpone")
436 end
437end
438
439implement {
440 name = "flushpostponednotes",
441 actions = flushpostponednotes
442}
443
444implement {
445 name = "flushnotes",
446 actions = flushnotes,
447 arguments = "3 strings",
448}
449
450function notes.resetpostponed()
451 if trace_notes then
452 report_notes("resetting all postponed notes")
453 end
454 for tag, state in next, notestates do
455 if state.kind == "postpone" then
456 state.start = nil
457 state.kind = "insert"
458 end
459 end
460end
461
462implement {
463 name = "notetitle",
464 actions = function(tag,n) lists.savedlisttitle(tag,notedata[tag][n]) end,
465 arguments = { "string", "integer" }
466}
467
468implement {
469 name = "noteprefixednumber",
470 actions = function(tag,n) lists.savedlistprefixednumber(tag,notedata[tag][n]) end,
471 arguments = { "string", "integer" }
472}
473
474function notes.internalid(tag,n)
475 local nd = get(tag,n)
476 if nd then
477 local r = nd.references
478 return r.internal
479 end
480end
481
482
483
484
485
486
487
488
489local report_insert = logs.reporter("pagebuilder","insert")
490local trace_insert = false trackers.register("pagebuilder.insert",function(v) trace_insert = v end)
491
492local texgetglue = tex.getglue
493local texsetglue = tex.setglue
494
495local function check_spacing(n,i)
496
497 local gn, pn, mn = texgetglue(n)
498 local gi, pi, mi = texgetglue(i > 1 and "s_strc_notes_inbetween" or "s_strc_notes_before")
499 local gt, pt, mt = gn + gi, pn + pi, mn + mi
500 if trace_insert then
501 report_insert("%s %i: %p plus %p minus %p","always ",n,gn,pn,mn)
502 report_insert("%s %i: %p plus %p minus %p",i > 1 and "inbetween" or "before ",n,gi,pi,mi)
503 report_insert("%s %i: %p plus %p minus %p","effective",n,gt,pt,mt)
504 end
505 return gt, pt, mt
506end
507
508notes.check_spacing = check_spacing
509
510callback.register("build_page_insert", function(n,i)
511 local state = notes.numbers[n]
512 if state then
513
514 local gt, pt, mt = check_spacing(n,i)
515 texsetglue(0,gt,pt,mt)
516 return 0
517 else
518 return n
519 end
520end)
521 |