1if not modules then modules = { } end modules ['attr-ini'] = {
2 version = 1.001,
3 comment = "companion to attr-ini.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 next, type = next, type
10local osexit = os.exit
11local sortedhash = table.sortedhash
12
13
14
15
16local nodes = nodes
17local context = context
18local storage = storage
19local commands = commands
20
21local implement = interfaces.implement
22
23attributes = attributes or { }
24local attributes = attributes
25
26local sharedstorage = storage.shared
27
28local texsetattribute = tex.setattribute
29
30attributes.names = attributes.names or { }
31attributes.numbers = attributes.numbers or { }
32attributes.list = attributes.list or { }
33attributes.values = attributes.values or { }
34attributes.counts = attributes.counts or { }
35attributes.handlers = attributes.handlers or { }
36attributes.states = attributes.states or { }
37attributes.unsetvalue = tex.magicconstants.unusedattributevalue
38
39local currentfont = font.current
40local currentattributes = nodes and nodes. currentattributes or node.currentattributes
41local getusedattributes = nodes and nodes.nuts and nodes.nuts.getusedattributes or node.direct.getusedattributes
42
43local names = attributes.names
44local numbers = attributes.numbers
45local list = attributes.list
46local values = attributes.values
47local counts = attributes.counts
48
49storage.register("attributes/names", names, "attributes.names")
50storage.register("attributes/numbers", numbers, "attributes.numbers")
51storage.register("attributes/list", list, "attributes.list")
52storage.register("attributes/values", values, "attributes.values")
53storage.register("attributes/counts", counts, "attributes.counts")
54
55local report_attribute = logs.reporter("attributes")
56local report_value = logs.reporter("attributes","values")
57
58local trace_values = false
59
60local max_register_index <const> = tex.magicconstants.max_attribute_register_index
61
62trackers.register("attributes.values", function(v) trace_values = v end)
63
64
65
66
67
68
69
70
71
72
73
74names[0], numbers["fontdynamic"] = "fontdynamic", 0
75
76
77
78
79
80sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 15
81sharedstorage.attributes_last_public = sharedstorage.attributes_last_public or 1024
82
83function attributes.private(name)
84 local number = numbers[name]
85 if not number then
86 local last = sharedstorage.attributes_last_private
87 if last < 1023 then
88 last = last + 1
89 sharedstorage.attributes_last_private = last
90 else
91 report_attribute("no more room for private attributes")
92 osexit()
93 end
94 number = last
95 numbers[name], names[number], list[number] = number, name, { }
96 end
97 return number
98end
99
100function attributes.public(name)
101 local number = numbers[name]
102 if not number then
103 local last = sharedstorage.attributes_last_public
104 if last < max_register_index then
105 last = last + 1
106 sharedstorage.attributes_last_public = last
107 else
108 report_attribute("no more room for public attributes")
109 osexit()
110 end
111 number = last
112 numbers[name], names[number], list[number] = number, name, { }
113 end
114 return number
115end
116
117attributes.system = attributes.private
118
119function attributes.define(name,category)
120 return (attributes[category or "public"] or attributes["public"])(name)
121end
122
123
124
125local function showlist(what,list)
126 if list then
127 local a = list.next
128 local i = 0
129 while a do
130 local number = a.index
131 local value = a.value
132 i = i + 1
133 report_attribute("%S %2i: attribute %3i, value %4i, name %a",what,i,number,value,names[number])
134 a = a.next
135 end
136 end
137end
138
139function attributes.showcurrent()
140 showlist("current",currentattributes())
141end
142
143function attributes.ofnode(n)
144 showlist(n,n.attr)
145end
146
147
148
149local store = { }
150
151function attributes.save(name)
152 name = name or ""
153 local n = currentattributes()
154 n = n and n.next
155 local t = { }
156 while n do
157 t[n.index] = n.value
158 n = n.next
159 end
160 store[name] = {
161 attr = t,
162 font = currentfont(),
163 }
164end
165
166function attributes.restore(name)
167 name = name or ""
168 local t = store[name]
169 if t then
170 local attr = t.attr
171 local font = t.font
172 if attr then
173 for k, v in next, attr do
174 texsetattribute(k,v)
175 end
176 end
177 if font then
178
179
180 currentfont(font)
181 end
182 end
183
184end
185
186
187
188local cleaners = { }
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204function attributes.registervalue(index,value)
205 local list = values[index]
206 local last
207 if list then
208 local c = counts[index]
209 if c and c[2] > 0 then
210
211 for i=c[1],c[2] do
212 if list[i] == nil then
213
214 local n = i == 0 and 1 or i
215 if trace_values then
216 report_value("reusing slot %i for attribute %i in range (%i,%i)",n,index,c[1],c[2])
217 end
218 c[1] = n
219 list[n] = value
220 return n
221 end
222 end
223 else
224 c = { 0, 0 }
225 end
226 last = c[2] + 1
227 list[last] = value
228 c[1] = last
229 c[2] = last
230 if trace_values then
231 report_value("expanding to slot %i for attribute %i",last,index)
232 end
233 else
234 last = 1
235 values[index] = { value }
236 counts[index] = { last, last }
237 if trace_values then
238 report_value("starting at slot %i for attribute %i",last,index)
239 end
240 end
241 return last
242end
243
244function attributes.getvalue(index,value)
245 local list = values[index]
246 return list and list[value] or nil
247end
248
249function attributes.hasvalues(index)
250 local list = values[index]
251 return list and next(list) and true or false
252end
253
254function attributes.getvalues(index)
255 local list = values[index]
256 return list and next(list) and list or nil
257end
258
259function attributes.setcleaner(index,cleaner)
260 cleaners[index] = cleaner
261end
262
263function attributes.checkvalues()
264
265
266
267
268 if next(values) then
269 local active = getusedattributes()
270 if trace_values then
271
272 for index, list in sortedhash(values) do
273 local b = active[index]
274 if b then
275 local cleaner = cleaners[index]
276 for k in sortedhash(list) do
277 if b[k] then
278 report_value("keeping value %i for attribute %i",k,index)
279 else
280 report_value("wiping value %i for attribute %i",k,index)
281 if cleaner then
282 cleaner(list[k])
283 end
284 list[k] = nil
285 end
286 end
287 if next(list) then
288 counts[index][1] = 0
289 goto continue
290 end
291 end
292 report_value("no more values for attribute %i",index)
293 values[index] = nil
294 counts[index] = nil
295 ::continue::
296 end
297 else
298 for index, list in next, values do
299 local b = active[index]
300 if b then
301 local cleaner = cleaners[index]
302 for k in next, list do
303 if not b[k] then
304 if cleaner then
305 cleaner(list[k])
306 end
307 list[k] = nil
308 end
309 end
310 if next(list) then
311 counts[index][1] = 0
312 goto continue
313 end
314 end
315 values[index] = nil
316 counts[index] = { 0, 0 }
317 ::continue::
318 end
319 end
320 elseif trace_values then
321 report_value("no check needed")
322 end
323end
324
325implement {
326 name = "cleanupattributes",
327
328 protected = true,
329 actions = attributes.checkvalues,
330}
331
332
333
334implement {
335 name = "defineattribute",
336 arguments = "2 strings",
337 actions = { attributes.define, context }
338}
339
340implement {
341 name = "showattributes",
342 actions = attributes.showcurrent
343}
344
345implement {
346 name = "savecurrentattributes",
347 arguments = "string",
348 actions = attributes.save
349}
350
351implement {
352 name = "restorecurrentattributes",
353 arguments = "string",
354 actions = attributes.restore
355}
356 |