1if not modules then modules = { } end modules [ ' strc-bkm ' ] = {
2 version = 0 . 200 ,
3 comment = " companion to strc-bkm.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
9
10
11
12
13
14
15
16
17
18local next , type = next , type
19local gsub , lower = string . gsub , string . lower
20local concat = table . concat
21local settings_to_hash = utilities . parsers . settings_to_hash
22
23local trace_bookmarks = false trackers . register ( " references.bookmarks " , function ( v ) trace_bookmarks = v end )
24local report_bookmarks = logs . reporter ( " structure " , " bookmarks " )
25
26local structures = structures
27
28structures . bookmarks = structures . bookmarks or { }
29
30local bookmarks = structures . bookmarks
31local sections = structures . sections
32local lists = structures . lists
33local levelmap = sections . levelmap
34local variables = interfaces . variables
35local implement = interfaces . implement
36local codeinjections = backends . codeinjections
37
38bookmarks . method = " internal "
39
40local names = { }
41local opened = { }
42local forced = { }
43local numbered = { }
44
45function bookmarks . setopened ( key , value )
46 if value = = nil then
47 value = true
48 end
49 if type ( key ) = = " table " then
50 for i = 1 , # key do
51 opened [ key [ i ] ] = value
52 end
53 else
54 opened [ key ] = value
55 end
56end
57
58function bookmarks . register ( settings )
59 local force = settings . force = = variables . yes
60 local number = settings . number = = variables . yes
61 local allopen = settings . opened = = variables . all
62 for k , v in next , settings_to_hash ( settings . names or " " ) do
63 names [ k ] = true
64 if force then
65 forced [ k ] = true
66 if allopen then
67 opened [ k ] = true
68 end
69 end
70 if number then
71 numbered [ k ] = true
72 end
73 end
74 if not allopen then
75 for k , v in next , settings_to_hash ( settings . opened or " " ) do
76 opened [ k ] = true
77 end
78 end
79end
80
81function bookmarks . overload ( name , text )
82 local l , ls = lists . tobesaved , nil
83 if # l = = 0 then
84
85 elseif name = = " " then
86 ls = l [ # l ]
87 else
88 for i = # l , 0 , -1 do
89 local li = l [ i ]
90 local metadata = li . metadata
91 if metadata and not metadata . nolist and metadata . name = = name then
92 ls = li
93 break
94 end
95 end
96 end
97 if ls then
98 local titledata = ls . titledata
99 if titledata then
100 titledata . bookmark = text
101 end
102 end
103
104
105end
106
107local function stripped ( str )
108 str = gsub ( str , " \\([A-Z]+) " , " %1 " )
109 str = gsub ( str , " \\ " , " " )
110 str = gsub ( str , " \\([A-Za-z]+) *{(.-)} " , " %2 " )
111 str = gsub ( str , " + " , " " )
112 return str
113end
114
115
116
117local numberspec = { }
118
119function bookmarks . setup ( spec )
120
121 for k , v in next , spec do
122 numberspec [ k ] = v
123 end
124end
125
126function bookmarks . place ( )
127 if next ( names ) then
128 local levels = { }
129 local noflevels = 0
130 local lastlevel = 1
131 local nofblocks = # lists . sectionblocks
132 local showblocktitle = toboolean ( numberspec . showblocktitle , true )
133
134 local allblocks = sections . sectionblockdata
135 for i = 1 , nofblocks do
136 local block = lists . sectionblocks [ i ]
137 local blockdone = nofblocks = = 1
138 local list = lists . filter {
139 names = names ,
140 criterium = block . . " :all " ,
141 forced = forced ,
142 }
143 for i = 1 , # list do
144 local li = list [ i ]
145 local metadata = li . metadata
146 local name = metadata . name
147 if not metadata . nolist or forced [ name ] then
148 local titledata = li . titledata
149
150 if not titledata then
151 local userdata = li . userdata
152 if userdata then
153 local first = userdata . first
154 local second = userdata . second
155 if first then
156 if second then
157 titledata = { title = first . . " " . . second }
158 else
159 titledata = { title = first }
160 end
161 elseif second then
162 titledata = { title = second }
163 else
164
165 end
166 end
167 end
168
169 if titledata then
170 if not blockdone then
171 if showblocktitle then
172
173 local blockdata = allblocks [ block ]
174 local references = li . references
175 noflevels = noflevels + 1
176 levels [ noflevels ] = {
177 level = 1 ,
178 title = stripped ( blockdata . bookmark ~ = " " and blockdata . bookmark or block ) ,
179 reference = references ,
180 opened = allopen or opened [ name ] ,
181 realpage = references and references . realpage or 0 ,
182 usedpage = true ,
183 }
184 end
185 blockdone = true
186 end
187 local structural = levelmap [ name ]
188 lastlevel = structural or lastlevel
189 if nofblocks > 1 then
190
191 lastlevel = lastlevel + 1
192 end
193 local title = titledata . bookmark
194 if not title or title = = " " then
195
196
197
198
199 title = titledata . title or " ? "
200
201 end
202
203
204
205
206
207
208
209
210
211
212
213
214
215if numbered [ name ] then
216 local numberdata = li . numberdata
217 if numberdata and not numberdata . hidenumber then
218
219 local number = sections . typesetnumber ( numberdata , " direct " , numberspec , numberdata )
220 if number and # number > 0 then
221 title = concat ( number ) . . " " . . title
222 end
223 end
224end
225 noflevels = noflevels + 1
226 local references = li . references
227 levels [ noflevels ] = {
228 level = lastlevel ,
229 title = stripped ( title ) ,
230 reference = references ,
231 opened = allopen or opened [ name ] ,
232 realpage = references and references . realpage or 0 ,
233 usedpage = true ,
234 structural = structural ,
235 name = name ,
236 }
237 end
238 end
239 end
240 end
241
242 bookmarks . finalize ( levels )
243 function bookmarks . place ( ) end
244 end
245end
246
247function bookmarks . flatten ( levels )
248 if not levels then
249
250 return { }
251 end
252
253
254
255
256 local noflevels = # levels
257 if noflevels > 1 then
258 local function showthem ( )
259 for i = 1 , noflevels do
260 local level = levels [ i ]
261
262
263
264 report_bookmarks ( " %i > %s > %s > %s " , level . level , level . reference . block , level . name , level . title )
265
266 end
267 end
268 if trace_bookmarks then
269 report_bookmarks ( " checking structure " )
270 showthem ( )
271 end
272 local skip = false
273 local done = 0
274 local start = 1
275 local one = levels [ 1 ]
276 local first = one . level
277 local block = one . reference . block
278 for i = 2 , noflevels do
279 local current = levels [ i ]
280 local new = current . level
281 local reference = current . reference
282 local newblock = type ( reference ) = = " table " and current . reference . block or block
283 if newblock ~ = block then
284 first = new
285 block = newblock
286 start = i
287 skip = false
288 elseif skip then
289
290 elseif new > first then
291 skip = true
292 elseif new < first then
293 for j = start , i -1 do
294 local previous = levels [ j ]
295 local old = previous . level
296 previous . level = new
297 if trace_bookmarks then
298 report_bookmarks ( " promoting entry %a from level %a to %a: %s " , j , old , new , previous . title )
299 end
300 done = done + 1
301 end
302 skip = true
303 end
304 end
305 if trace_bookmarks then
306 if done > 0 then
307 report_bookmarks ( " %a entries promoted " )
308 showthem ( )
309 else
310 report_bookmarks ( " nothing promoted " )
311 end
312 end
313 end
314 return levels
315end
316
317local extras = { }
318local lists = { }
319local names = { }
320
321bookmarks . extras = extras
322
323local function cleanname ( name )
324 return lower ( file . basename ( name ) )
325end
326
327function extras . register ( name , levels )
328 if name and levels then
329 name = cleanname ( name )
330 local found = names [ name ]
331 if found then
332 lists [ found ] . levels = levels
333 else
334 lists [ # lists + 1 ] = {
335 name = name ,
336 levels = levels ,
337 }
338 names [ name ] = # lists
339 end
340 end
341end
342
343function extras . get ( name )
344 if name then
345 local found = names [ cleanname ( name ) ]
346 if found then
347 return lists [ found ] . levels
348 end
349 else
350 return lists
351 end
352end
353
354function extras . reset ( name )
355 local l , n = { } , { }
356 if name then
357 name = cleanname ( name )
358 for i = 1 , # lists do
359 local li = lists [ i ]
360 local ln = li . name
361 if name = = ln then
362
363 else
364 local m = # l + 1
365 l [ m ] = li
366 n [ ln ] = m
367 end
368 end
369 end
370 lists , names = l , n
371end
372
373local function checklists ( )
374 for i = 1 , # lists do
375 local levels = lists [ i ] . levels
376 for j = 1 , # levels do
377 local entry = levels [ j ]
378 local pageindex = entry . pageindex
379 if pageindex then
380 entry . reference = figures . getrealpage ( pageindex )
381 entry . pageindex = nil
382 end
383 end
384 end
385end
386
387function extras . tosections ( levels )
388 local sections = { }
389 local noflists = # lists
390 for i = 1 , noflists do
391 local levels = lists [ i ] . levels
392 local data = { }
393 sections [ i ] = data
394 for j = 1 , # levels do
395 local entry = levels [ j ]
396 if entry . usedpage then
397 local section = entry . section
398 local d = data [ section ]
399 if d then
400 d [ # d + 1 ] = entry
401 else
402 data [ section ] = { entry }
403 end
404 end
405 end
406 end
407 return sections
408end
409
410function extras . mergesections ( levels , sections )
411 if not sections or # sections = = 0 then
412 return levels
413 elseif not levels then
414 return { }
415 else
416 local merge = { }
417 local noflists = # lists
418 if # levels = = 0 then
419 local level = 0
420 local section = 0
421 for i = 1 , noflists do
422 local entries = sections [ i ] [ 0 ]
423 if entries then
424 for i = 1 , # entries do
425 local entry = entries [ i ]
426 merge [ # merge + 1 ] = entry
427 entry . level = entry . level + level
428 end
429 end
430 end
431 else
432 for j = 1 , # levels do
433 local entry = levels [ j ]
434 merge [ # merge + 1 ] = entry
435 local section = entry . reference . section
436 local level = entry . level
437 entry . section = section
438 for i = 1 , noflists do
439 local entries = sections [ i ] [ section ]
440 if entries then
441 for i = 1 , # entries do
442 local entry = entries [ i ]
443 merge [ # merge + 1 ] = entry
444 entry . level = entry . level + level
445 end
446 end
447 end
448 end
449 end
450 return merge
451 end
452end
453
454function bookmarks . merge ( levels , mode )
455 return extras . mergesections ( levels , extras . tosections ( ) )
456end
457
458local sequencers = utilities . sequencers
459local appendgroup = sequencers . appendgroup
460local appendaction = sequencers . appendaction
461
462local bookmarkactions = sequencers . new {
463 arguments = " levels,method " ,
464 returnvalues = " levels " ,
465 results = " levels " ,
466}
467
468appendgroup ( bookmarkactions , " before " )
469appendgroup ( bookmarkactions , " system " )
470appendgroup ( bookmarkactions , " after " )
471
472appendaction ( bookmarkactions , " system " , bookmarks . flatten )
473appendaction ( bookmarkactions , " system " , bookmarks . merge )
474
475function bookmarks . finalize ( levels )
476 local method = bookmarks . method or " internal "
477 checklists ( )
478 levels = bookmarkactions . runner ( levels , method )
479 if levels and # levels > 0 then
480
481 local purged = { }
482 for i = 1 , # levels do
483 local l = levels [ i ]
484 if l . usedpage ~ = false then
485 purged [ # purged + 1 ] = l
486 end
487 end
488
489 codeinjections . addbookmarks ( purged , method )
490 else
491
492 end
493end
494
495function bookmarks . installhandler ( what , where , func )
496 if not func then
497 where , func = " after " , where
498 end
499 if where = = " before " or where = = " after " then
500 sequencers . appendaction ( bookmarkactions , where , func )
501 else
502 report_tex ( " installing bookmark %a handlers in %a is not possible " , what , tostring ( where ) )
503 end
504end
505
506
507
508implement {
509 name = " setupbookmarks " ,
510 actions = bookmarks . setup ,
511 arguments = {
512 {
513 { " separatorset " } ,
514 { " conversionset " } ,
515 { " starter " } ,
516 { " stopper " } ,
517 { " segments " } ,
518 { " showblocktitle " } ,
519 }
520 }
521}
522
523implement {
524 name = " registerbookmark " ,
525 actions = bookmarks . register ,
526 arguments = {
527 {
528 { " names " } ,
529 { " opened " } ,
530 { " force " } ,
531 { " number " } ,
532 }
533 }
534}
535
536implement {
537 name = " overloadbookmark " ,
538 actions = bookmarks . overload ,
539 arguments = " 2 strings " ,
540}
541 |