xref: /inferno-os/appl/spree/lib/cardlib.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Cardlib;
2include "sys.m";
3	sys: Sys;
4include "draw.m";
5include "sets.m";
6	sets: Sets;
7	Set, set, A, B, All, None: import sets;
8include "../spree.m";
9	spree: Spree;
10	Attributes, Range, Object, Clique, Member, rand: import spree;
11include "objstore.m";
12	objstore: Objstore;
13include "cardlib.m";
14
15MAXPLAYERS: con 4;
16
17Layobject: adt {
18	lay:		ref Object;
19	name:	string;
20	packopts:		int;
21	pick {
22	Obj =>
23		obj:		ref Object;		# nil if it's a frame
24	Frame =>
25		facing:	int;				# only valid if for frames
26	}
27};
28
29clique:	ref Clique;
30cmembers: array of ref Cmember;
31cpids := array[8] of list of ref Cmember;
32
33# XXX first string is unnecessary as it's held in the Layobject anyway?
34layouts := array[17] of list of (string, ref Layout, ref Layobject);
35maxlayid := 1;
36cmemberid := 1;
37
38archiveobjs: array of list of (string, ref Object);
39
40defaultrank := array[13] of {12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
41defaultsuitrank := array[] of {CLUBS => 0, DIAMONDS => 1, HEARTS => 2, SPADES => 3};
42
43table := array[] of {
44	0 =>	array[] of {
45		(-1, dTOP|EXPAND, dBOTTOM, dTOP),
46	},
47	1 => array [] of {
48		(0, dBOTTOM|FILLX, dBOTTOM, dTOP),
49		(-1, dTOP|EXPAND, dBOTTOM, dTOP),
50	},
51	2 => array[] of {
52		(0, dBOTTOM|FILLX, dBOTTOM, dTOP),
53		(1, dTOP|FILLX, dTOP, dBOTTOM),
54		(-1, dTOP|EXPAND, dBOTTOM, dTOP)
55	},
56	3 => array[] of {
57		(2, dRIGHT|FILLY, dRIGHT, dLEFT),
58		(0, dBOTTOM|FILLX, dBOTTOM, dTOP),
59		(1, dTOP|FILLX, dTOP, dBOTTOM),
60		(-1, dRIGHT|EXPAND, dBOTTOM, dTOP)
61	},
62	4 => array[] of {
63		(1, dLEFT|FILLY, dLEFT, dRIGHT),
64		(3, dRIGHT|FILLY, dRIGHT, dLEFT),
65		(0, dBOTTOM|FILLX, dBOTTOM, dTOP),
66		(2, dTOP|FILLX, dTOP, dBOTTOM),
67		(-1, dRIGHT|EXPAND, dBOTTOM, dTOP)
68	},
69};
70
71
72init(mod: Spree, g: ref Clique)
73{
74	sys = load Sys Sys->PATH;
75	sets = load Sets Sets->PATH;
76	if (sets == nil)
77		panic(sys->sprint("cannot load %s: %r", Sets->PATH));
78	objstore = load Objstore Objstore->PATH;
79	if (objstore == nil)
80		panic(sys->sprint("cannot load %s: %r", Objstore->PATH));
81	objstore->init(mod, g);
82	clique = g;
83	spree = mod;
84}
85
86archive(): ref Object
87{
88	for (i := 0; i < len cmembers; i++) {
89		cp := cmembers[i];
90		setarchivename(cp.obj, "member" + string i);
91		setarchivename(cp.layout.lay, "layout" + string i);
92		sel := cp.sel;
93		if (sel.stack != nil)
94			setarchivename(sel.stack, "sel" + string i);
95	}
96	for (i = 0; i < len layouts; i++) {
97		for (ll := layouts[i]; ll != nil; ll = tl ll) {
98			(name, lay, layobj) := hd ll;
99			if (name != nil)
100				layobj.lay.setattr("layname", name, None);
101			pick l := layobj {
102			Frame =>
103				l.lay.setattr("facing", sides[l.facing], None);
104			Obj =>
105				setarchivename(l.obj, "layid" + l.obj.getattr("layid"));
106			}
107		}
108	}
109	# XXX should archive layouts that aren't particular to a member.
110	archiveobj := clique.newobject(nil, None, "archive");
111	setarchivename(archiveobj, "archive");
112	archiveobj.setattr("maxlayid", string maxlayid, None);
113	archiveobj.setattr("cmemberid", string cmemberid, None);
114	return archiveobj;
115}
116
117setarchivename(o: ref Object, name: string)
118{
119	objstore->setname(o, name);
120}
121
122getarchiveobj(name: string): ref Object
123{
124	return objstore->get(name);
125}
126
127archivearray(a: array of ref Object, name: string)
128{
129	for (i := 0; i < len a; i++)
130		objstore->setname(a[i], name + string i);
131}
132
133getarchivearray(name: string): array of ref Object
134{
135	l: list of ref Object;
136	for (i := 0; ; i++) {
137		o := objstore->get(name + string i);
138		if (o == nil)
139			break;
140		l = o :: l;
141	}
142	a := array[i] of ref Object;
143	for (; l != nil; l = tl l)
144		a[--i] = hd l;
145	return a;
146}
147
148unarchive(): ref Object
149{
150	objstore->unarchive();
151	archiveobj := getarchiveobj("archive");
152	cpl: list of ref Cmember;
153	for (i := 0; (o := getarchiveobj("member" + string i)) != nil; i++) {
154		cp := ref Cmember(
155			i,
156			int o.getattr("id"),
157			clique.membernamed(o.getattr("name")),
158			o,
159			ref Layout(getarchiveobj("layout" + string i)),
160			ref Selection(getarchiveobj("sel" + string i), -1, 1, (0, 0), nil)
161		);
162		cp.sel.ownerid = cp.id;
163		sel := cp.sel;
164		if (sel.stack != nil && (selstr := sel.stack.getattr("sel")) != nil) {
165			(n, val) := sys->tokenize(selstr, " ");
166			if (tl val != nil && hd tl val == "-")
167				(sel.r.start, sel.r.end) = (int hd val, int hd tl tl val);
168			else {
169				idxl: list of int;
170				sel.isrange = 0;
171				for (; val != nil; val = tl val)
172					idxl = int hd val :: idxl;
173				sel.idxl = idxl;
174			}
175		}
176		lay := cp.layout.lay;
177		# there should be exactly one child, of type "layframe"
178		if (len lay.children != 1 || lay.children[0].objtype != "layframe")
179			panic("invalid layout");
180		x := strhash(nil, len layouts);
181		layouts[x] = (nil, cp.layout, obj2layobj(lay.children[0])) :: layouts[x];
182		unarchivelayoutobj(cp.layout, lay.children[0]);
183		cpl = cp :: cpl;
184	}
185	cmembers = array[len cpl] of ref Cmember;
186	for (; cpl != nil; cpl = tl cpl) {
187		cp := hd cpl;
188		cmembers[cp.ord] = cp;
189		idx := cp.id % len cpids;
190		cpids[idx] = cp :: cpids[idx];
191	}
192
193	maxlayid = int archiveobj.getattr("maxlayid");
194	cmemberid = int archiveobj.getattr("cmemberid");
195	return archiveobj;
196}
197
198unarchivelayoutobj(layout: ref Layout, o: ref Object)
199{
200	for (i := 0; i < len o.children; i++) {
201		child := o.children[i];
202		layobj := obj2layobj(child);
203		if (layobj.name != nil) {
204			x := strhash(layobj.name, len layouts);
205			layouts[x] = (layobj.name, layout, layobj) :: layouts[x];
206		}
207		if (tagof(layobj) == tagof(Layobject.Frame))
208			unarchivelayoutobj(layout, child);
209	}
210}
211
212obj2layobj(o: ref Object): ref Layobject
213{
214	case o.objtype {
215	"layframe" =>
216		return ref Layobject.Frame(
217			o,
218			o.getattr("layname"),
219			s2packopts(o.getattr("opts")),
220			searchopt(sides, o.getattr("facing"))
221		);
222	"layobj" =>
223		return ref Layobject.Obj(
224			o,
225			o.getattr("layname"),
226			s2packopts(o.getattr("opts")),
227			getarchiveobj("layid" + o.getattr("layid"))
228		);
229	* =>
230		panic("invalid layobject found, of type '" + o.objtype + "'");
231		return nil;
232	}
233}
234
235Cmember.join(member: ref Member, ord: int): ref Cmember
236{
237	cmembers = (array[len cmembers + 1] of ref Cmember)[0:] = cmembers;
238	if (ord == -1)
239		ord = len cmembers - 1;
240	else {
241		cmembers[ord + 1:] = cmembers[ord:len cmembers - 1];
242		for (i := ord + 1; i < len cmembers; i++)
243			cmembers[i].ord = i;
244	}
245	cp := cmembers[ord] = ref Cmember(ord, cmemberid++, member, nil, nil, nil);
246	cp.obj = clique.newobject(nil, All, "member");
247	cp.obj.setattr("id", string cp.id, All);
248	cp.obj.setattr("name", member.name, All);
249	cp.obj.setattr("you", string cp.id, None.add(member.id));
250	cp.obj.setattr("cliquetitle", clique.fname, All);
251	cp.layout = newlayout(cp.obj, None.add(member.id));
252	cp.sel = ref Selection(nil, cp.id, 1, (0, 0), nil);
253
254	idx := cp.id % len cpids;
255	cpids[idx] = cp :: cpids[idx];
256	return cp;
257}
258
259Cmember.find(p: ref Member): ref Cmember
260{
261	id := p.id;
262	for (i := 0; i < len cmembers; i++)
263		if (cmembers[i].p.id == id)
264			return cmembers[i];
265	return nil;
266}
267
268Cmember.index(ord: int): ref Cmember
269{
270	if (ord < 0 || ord >= len cmembers)
271		return nil;
272	return cmembers[ord];
273}
274
275Cmember.next(cp: self ref Cmember, fwd: int): ref Cmember
276{
277	if (!fwd)
278		return cp.prev(1);
279	x := cp.ord + 1;
280	if (x >= len cmembers)
281		x = 0;
282	return cmembers[x];
283}
284
285Cmember.prev(cp: self ref Cmember, fwd: int): ref Cmember
286{
287	if (!fwd)
288		return cp.next(1);
289	x := cp.ord - 1;
290	if (x < 0)
291		x = len cmembers - 1;
292	return cmembers[x];
293}
294
295Cmember.leave(cp: self ref Cmember)
296{
297	ord := cp.ord;
298	cmembers[ord] = nil;
299	cmembers[ord:] = cmembers[ord + 1:];
300	cmembers[len cmembers - 1] = nil;
301	cmembers = cmembers[0:len cmembers - 1];
302	for (i := ord; i < len cmembers; i++)
303		cmembers[i].ord = i;
304	cp.obj.delete();
305	dellayout(cp.layout);
306	cp.layout = nil;
307	idx := cp.id % len cpids;
308	l: list of ref Cmember;
309	ll := cpids[idx];
310	for (; ll != nil; ll = tl ll)
311		if (hd ll != cp)
312			l = hd ll :: l;
313	cpids[idx] = l;
314	cp.ord = -1;
315}
316
317Cmember.findid(id: int): ref Cmember
318{
319	for (l := cpids[id % len cpids]; l != nil; l = tl l)
320		if ((hd l).id == id)
321			return hd l;
322	return nil;
323}
324
325newstack(parent: ref Object, owner: ref Member, spec: Stackspec): ref Object
326{
327	vis := All;
328	if (spec.conceal) {
329		vis = None;
330		if (owner != nil)
331			vis = vis.add(owner.id);
332	}
333	o := clique.newobject(parent, vis, "stack");
334	o.setattr("maxcards", string spec.maxcards, All);
335	o.setattr("style", spec.style, All);
336
337	# XXX provide some means for this to contain the member's name?
338	o.setattr("title", spec.title, All);
339	return o;
340}
341
342makecard(deck: ref Object, c: Card, rear: string): ref Object
343{
344	card := clique.newobject(deck, None, "card");
345	card.setattr("face", string c.face, All);
346	vis := None;
347	if(c.face)
348		vis = All;
349	card.setattr("number", string (c.number * 4 + c.suit), vis);
350	if (rear != nil)
351		card.setattr("rear", rear, All);
352	return card;
353}
354
355makecards(deck: ref Object, r: Range, rear: string)
356{
357	for (i := r.start; i < r.end; i++)
358		for(suit := 0; suit < 4; suit++)
359			makecard(deck, (suit, i, 0), rear);
360}
361
362# deal n cards to each member, if possible.
363# deal in chunks for efficiency.
364# if accuracy is required (e.g. dealing from an unshuffled
365# deck containing known cards) then this'll have to change.
366deal(deck: ref Object, n: int, stacks: array of ref Object, first: int)
367{
368	ncards := len deck.children;
369	ord := 0;
370	permember := n;
371	leftover := 0;
372	if (n * len stacks > ncards) {
373		# if trying to deal more cards than we've got,
374		# deal all that we've got, distributing the remainder fairly.
375		permember = ncards / len stacks;
376		leftover = ncards % len stacks;
377	}
378	for (i := 0; i < len stacks; i++) {
379		n = permember;
380		if (leftover > 0) {
381			n++;
382			leftover--;
383		}
384		priv := stacks[(first + i) % len stacks];
385		deck.transfer((ncards - n, ncards), priv, len priv.children);
386		priv.setattr("n", string (int priv.getattr("n") + n), All);
387		# make cards visible to member
388		for (j := len priv.children - n; j < len priv.children; j++)
389			setface(priv.children[j], 1);
390
391		ncards -= n;
392	}
393}
394
395setface(card: ref Object, face: int)
396{
397	# XXX check parent stack style and if it's a pile,
398	# only expose a face up card at the top.
399
400	card.setattr("face", string face, All);
401	if (face)
402		card.setattrvisibility("number", All);
403	else
404		card.setattrvisibility("number", None);
405}
406
407nmembers(): int
408{
409	return len cmembers;
410}
411
412getcard(card: ref Object): Card
413{
414	n := int card.getattr("number");
415	(suit, num) := (n % 4, n / 4);
416	return Card(suit, num, int card.getattr("face"));
417}
418
419getcards(stack: ref Object): array of Card
420{
421	a := array[len stack.children] of Card;
422	for (i := 0; i < len a; i++)
423		a[i] = getcard(stack.children[i]);
424	return a;
425}
426
427discard(stk, pile: ref Object, facedown: int)
428{
429	n := len stk.children;
430	if (facedown)
431		for (i := 0; i < n; i++)
432			setface(stk.children[i], 0);
433	stk.transfer((0, n), pile, len pile.children);
434}
435
436# shuffle children into a random order.  first we make all the children
437# invisible (which will cause them to be deleted in the clients) then
438# shuffle to our heart's content, and make visible again...
439shuffle(o: ref Object)
440{
441	ovis := o.visibility;
442	o.setvisibility(None);
443	a := o.children;
444	n := len a;
445	for (i := 0; i < n; i++) {
446		j := i + rand(n - i);
447		(a[i], a[j]) = (a[j], a[i]);
448	}
449	o.setvisibility(ovis);
450}
451
452sort(o: ref Object, rank, suitrank: array of int)
453{
454	if (rank == nil)
455		rank = defaultrank;
456	if (suitrank == nil)
457		suitrank = defaultsuitrank;
458	ovis := o.visibility;
459	o.setvisibility(None);
460	cardmergesort(o.children, array[len o.children] of ref Object, rank, suitrank);
461	o.setvisibility(ovis);
462}
463
464cardcmp(a, b: ref Object, rank, suitrank: array of int): int
465{
466	c1 := getcard(a);
467	c2 := getcard(b);
468	if (suitrank[c1.suit] != suitrank[c2.suit])
469		return suitrank[c1.suit] - suitrank[c2.suit];
470	return rank[c1.number] - rank[c2.number];
471}
472
473cardmergesort(a, b: array of ref Object, rank, suitrank: array of int)
474{
475	r := len a;
476	if (r > 1) {
477		m := (r-1)/2 + 1;
478		cardmergesort(a[0:m], b[0:m], rank, suitrank);
479		cardmergesort(a[m:], b[m:], rank, suitrank);
480		b[0:] = a;
481		for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
482			if (cardcmp(b[i], b[j], rank, suitrank) > 0)
483				a[k] = b[j++];
484			else
485				a[k] = b[i++];
486		}
487		if (i < m)
488			a[k:] = b[i:m];
489		else if (j < r)
490			a[k:] = b[j:r];
491	}
492}
493
494# reverse and flip all cards in stack.
495flip(stack: ref Object)
496{
497	ovis := stack.visibility;
498	stack.setvisibility(None);
499	a := stack.children;
500	(n, m) := (len a, len a / 2);
501	for (i := 0; i < m; i++) {
502		j := n - i - 1;
503		(a[i], a[j]) = (a[j], a[i]);
504	}
505	for (i = 0; i < n; i++)
506		setface(a[i], !int a[i].getattr("face"));
507	stack.setvisibility(ovis);
508}
509
510selection(stack: ref Object): ref Selection
511{
512	if ((owner := stack.getattr("owner")) != nil &&
513			(cp := Cmember.findid(int owner)) != nil)
514		return cp.sel;
515	return nil;
516}
517
518Selection.set(sel: self ref Selection, stack: ref Object)
519{
520	if (stack == sel.stack)
521		return;
522	if (stack != nil) {
523		oldowner := stack.getattr("owner");
524		if (oldowner != nil) {
525			oldcp := Cmember.findid(int oldowner);
526			if (oldcp != nil)
527				oldcp.sel.set(nil);
528		}
529	}
530	if (sel.stack != nil)
531		sel.stack.setattr("owner", nil, All);
532	sel.stack = stack;
533	sel.isrange = 1;
534	sel.r = (0, 0);
535	sel.idxl = nil;
536	setsel(sel);
537}
538
539Selection.setexcl(sel: self ref Selection, stack: ref Object): int
540{
541	if (stack != nil && (oldowner := stack.getattr("owner")) != nil)
542		if ((cp := Cmember.findid(int oldowner)) != nil && !cp.sel.isempty())
543			return 0;
544	sel.set(stack);
545	return 1;
546}
547
548Selection.owner(sel: self ref Selection): ref Cmember
549{
550	return Cmember.findid(sel.ownerid);
551}
552
553Selection.setrange(sel: self ref Selection, r: Range)
554{
555	if (!sel.isrange) {
556		sel.idxl = nil;
557		sel.isrange = 1;
558	}
559	sel.r = r;
560	setsel(sel);
561}
562
563Selection.addindex(sel: self ref Selection, i: int)
564{
565	if (sel.isrange) {
566		sel.r = (0, 0);
567		sel.isrange = 0;
568	}
569	ll: list of int;
570	for (l := sel.idxl; l != nil; l = tl l) {
571		if (hd l >= i)
572			break;
573		ll = hd l :: ll;
574	}
575	if (l != nil && hd l == i)
576		return;
577	l = i :: l;
578	for (; ll != nil; ll = tl ll)
579		l = hd ll :: l;
580	sel.idxl = l;
581	setsel(sel);
582}
583
584Selection.delindex(sel: self ref Selection, i: int)
585{
586	if (sel.isrange) {
587		sys->print("cardlib: delindex from range-type selection\n");
588		return;
589	}
590	ll: list of int;
591	for (l := sel.idxl; l != nil; l = tl l) {
592		if (hd l == i) {
593			l = tl l;
594			break;
595		}
596		ll = hd l :: ll;
597	}
598	for (; ll != nil; ll = tl ll)
599		l = hd ll :: l;
600	sel.idxl = l;
601	setsel(sel);
602}
603
604Selection.isempty(sel: self ref Selection): int
605{
606	if (sel.stack == nil)
607		return 1;
608	if (sel.isrange)
609		return sel.r.start == sel.r.end;
610	return sel.idxl == nil;
611}
612
613Selection.isset(sel: self ref Selection, index: int): int
614{
615	if (sel.isrange)
616		return index >= sel.r.start && index < sel.r.end;
617	for (l := sel.idxl; l != nil; l = tl l)
618		if (hd l == index)
619			return 1;
620	return 0;
621}
622
623Selection.transfer(sel: self ref Selection, dst: ref Object, index: int)
624{
625	if (sel.isempty())
626		return;
627	src := sel.stack;
628	if (sel.isrange) {
629		r := sel.r;
630		sel.set(nil);
631		src.transfer(r, dst, index);
632	} else {
633		if (sel.stack == dst) {
634			sys->print("cardlib: cannot move multisel to same stack\n");
635			return;
636		}
637		xl := l := sel.idxl;
638		sel.set(nil);
639		rl: list of Range;
640		for (; l != nil; l = tl l) {
641			r := Range(hd l, hd l);
642			last := l;
643			# concatenate adjacent items, for efficiency.
644			for (l = tl l; l != nil; (last, l) = (l, tl l)) {
645				if (hd l != r.end + 1)
646					break;
647				r.end = hd l;
648			}
649			rl = (r.start, r.end + 1) :: rl;
650			l = last;
651		}
652		# do ranges in reverse, so that later ranges
653		# aren't affected by earlier ones.
654		if (index == -1)
655			index = len dst.children;
656		for (; rl != nil; rl = tl rl)
657			src.transfer(hd rl, dst, index);
658	}
659}
660
661setsel(sel: ref Selection)
662{
663	if (sel.stack == nil)
664		return;
665	s := "";
666	if (sel.isrange) {
667		if (sel.r.end > sel.r.start)
668			s = string sel.r.start + " - " + string sel.r.end;
669	} else {
670		if (sel.idxl != nil) {
671			s = string hd sel.idxl;
672			for (l := tl sel.idxl; l != nil; l = tl l)
673				s += " " + string hd l;
674		}
675	}
676	if (s != nil)
677		sel.stack.setattr("owner", string sel.owner().id, All);
678	else
679		sel.stack.setattr("owner", nil, All);
680	vis := None.add(sel.owner().p.id);
681	sel.stack.setattr("sel", s, vis);
682	sel.stack.setattrvisibility("sel", vis);
683}
684
685newlayout(parent: ref Object, vis: Set): ref Layout
686{
687	l := ref Layout(clique.newobject(parent, vis, "layout"));
688	x := strhash(nil, len layouts);
689	layobj := ref Layobject.Frame(nil, "", dTOP|EXPAND|FILLX|FILLY, dTOP);
690	layobj.lay = clique.newobject(l.lay, All, "layframe");
691	layobj.lay.setattr("opts", packopts2s(layobj.packopts), All);
692	layouts[x] = (nil, l, layobj) :: layouts[x];
693#	sys->print("[%d] => ('%s', %ux, %ux) (new layout)\n", x, "", l, layobj);
694	return l;
695}
696
697addlayframe(name, parent: string, layout: ref Layout, packopts: int, facing: int)
698{
699#	sys->print("addlayframe('%s', %ux, name: %s\n", parent, layout, name);
700	addlay(parent, layout, ref Layobject.Frame(nil, name, packopts, facing));
701}
702
703addlayobj(name, parent: string, layout: ref Layout, packopts: int, obj: ref Object)
704{
705#	sys->print("addlayobj('%s', %ux, name: %s, obj %d\n", parent, layout, name, obj.id);
706	addlay(parent, layout, ref Layobject.Obj(nil, name, packopts, obj));
707}
708
709addlay(parent: string, layout: ref Layout, layobj: ref Layobject)
710{
711	a := layouts;
712	name := layobj.name;
713	x := strhash(name, len a);
714	added := 0;
715	for (nl := a[strhash(parent, len a)]; nl != nil; nl = tl nl) {
716		(s, lay, parentlay) := hd nl;
717		if (s == parent && (layout == nil || layout == lay)) {
718			pick p := parentlay {
719			Obj =>
720				sys->fprint(sys->fildes(2),
721					"cardlib: cannot add layout to non-frame: %d\n", p.obj.id);
722			Frame =>
723				nlayobj := copylayobj(layobj);
724				nlayobj.packopts = packoptsfacing(nlayobj.packopts, p.facing);
725				o: ref Object;
726				pick lo := nlayobj {
727				Obj =>
728					o = clique.newobject(p.lay, All, "layobj");
729					id := lo.obj.getattr("layid");
730					if (id == nil) {
731						id = string maxlayid++;
732						lo.obj.setattr("layid", id, All);
733					}
734					o.setattr("layid", id, All);
735				Frame =>
736					o = clique.newobject(p.lay, All, "layframe");
737					lo.facing = (lo.facing + p.facing) % 4;
738				}
739				o.setattr("opts", packopts2s(nlayobj.packopts), All);
740				nlayobj.lay = o;
741				if (name != nil)
742					a[x] = (name, lay, nlayobj) :: a[x];
743				added++;
744			}
745		}
746	}
747	if (added == 0)
748		sys->print("no parent found, adding '%s', parent '%s', layout %ux\n",
749			layobj.name, parent, layout);
750#	sys->print("%d new entries\n", added);
751}
752
753maketable(parent: string)
754{
755	# make a table for all current members.
756	plcount := len cmembers;
757	packopts := table[plcount];
758	for (i := 0; i < plcount; i++) {
759		layout := cmembers[i].layout;
760		for (j := 0; j < len packopts; j++) {
761			(ord, outer, inner, facing) := packopts[j];
762			name := "public";
763			if (ord != -1)
764				name = "p" + string ((ord + i) % plcount);
765			addlayframe("@" + name, parent, layout, outer, dTOP);
766			addlayframe(name, "@" + name, layout, inner, facing);
767		}
768	}
769}
770
771dellay(name: string, layout: ref Layout)
772{
773	a := layouts;
774	x := strhash(name, len a);
775	rl: list of (string, ref Layout, ref Layobject);
776	for (nl := a[x]; nl != nil; nl = tl nl) {
777		(s, lay, layobj) := hd nl;
778		if (s != name || (layout != nil && layout != lay))
779			rl = hd nl :: rl;
780	}
781	a[x] = rl;
782}
783
784dellayout(layout: ref Layout)
785{
786	for (i := 0; i < len layouts; i++) {
787		ll: list of (string, ref Layout, ref Layobject);
788		for (nl := layouts[i]; nl != nil; nl = tl nl) {
789			(s, lay, layobj) := hd nl;
790			if (lay != layout)
791				ll = hd nl :: ll;
792		}
793		layouts[i] = ll;
794	}
795}
796
797copylayobj(obj: ref Layobject): ref Layobject
798{
799	pick o := obj {
800	Frame =>
801		return ref *o;
802	Obj =>
803		return ref *o;
804	}
805	return nil;
806}
807
808packoptsfacing(opts, facing: int): int
809{
810	if (facing == dTOP)
811		return opts;
812	nopts := 0;
813
814	# 4 directions
815	nopts |= (facing + (opts & dMASK)) % 4;
816
817	# 2 orientations
818	nopts |= ((facing + ((opts & oMASK) >> oSHIFT)) % 4) << oSHIFT;
819
820	# 8 anchorpoints (+ centre)
821	a := (opts & aMASK);
822	if (a != aCENTRE)
823		a = ((((a >> aSHIFT) - 1 + facing * 2) % 8) + 1) << aSHIFT;
824	nopts |= a;
825
826	# two fill options
827	if (facing % 2) {
828		if (opts & FILLX)
829			nopts |= FILLY;
830		if (opts & FILLY)
831			nopts |= FILLX;
832	} else
833		nopts |= (opts & (FILLX | FILLY));
834
835	nopts |= (opts & EXPAND);
836	return nopts;
837}
838
839# these arrays are dependent on the ordering of
840# the relevant constants defined in cardlib.m
841
842sides := array[] of {"top", "left", "bottom", "right"};
843anchors := array[] of {"centre", "n", "nw", "w", "sw", "s", "se", "e", "ne"};
844orientations := array[] of {"right", "up", "left", "down"};
845fills := array[] of {"none", "x", "y", "both"};
846
847packopts2s(opts: int): string
848{
849	s := orientations[(opts & oMASK) >> oSHIFT] +
850			" -side " + sides[opts & dMASK];
851	if ((opts & aMASK) != aCENTRE)
852		s += " -anchor " + anchors[(opts & aMASK) >> aSHIFT];
853	if (opts & EXPAND)
854		s += " -expand 1";
855	if (opts & (FILLX | FILLY))
856		s += " -fill " + fills[(opts & FILLMASK) >> FILLSHIFT];
857	return s;
858}
859
860searchopt(a: array of string, s: string): int
861{
862	for (i := 0; i < len a; i++)
863		if (a[i] == s)
864			return i;
865	panic("unknown pack option '" + s + "'");
866	return 0;
867}
868
869s2packopts(s: string): int
870{
871	(nil, toks) := sys->tokenize(s, " ");
872	if (toks == nil)
873		panic("invalid packopts: " + s);
874	p := searchopt(orientations, hd toks) << oSHIFT;
875	for (toks = tl toks; toks != nil; toks = tl tl toks) {
876		if (tl toks == nil)
877			panic("invalid packopts: " + s);
878		arg := hd tl toks;
879		case hd toks {
880		"-anchor" =>
881			p |= searchopt(anchors, arg) << aSHIFT;
882		"-fill" =>
883			p |= searchopt(fills, arg) << FILLSHIFT;
884		"-side" =>
885			p |= searchopt(sides, arg) << dSHIFT;
886		"-expand" =>
887			if (int hd tl toks)
888				p |= EXPAND;
889		* =>
890			panic("unknown pack option: " + hd toks);
891		}
892	}
893	return p;
894}
895
896panic(e: string)
897{
898	sys->fprint(sys->fildes(2), "cardlib panic: %s\n", e);
899	raise "panic";
900}
901
902assert(b: int, err: string)
903{
904	if (b == 0)
905		raise "parse:" + err;
906}
907
908# from Aho Hopcroft Ullman
909strhash(s: string, n: int): int
910{
911	h := 0;
912	m := len s;
913	for(i := 0; i<m; i++){
914		h = 65599 * h + s[i];
915	}
916	return (h & 16r7fffffff) % n;
917}
918