summaryrefslogtreecommitdiff
path: root/appl/spree/lib/cardlib.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/spree/lib/cardlib.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/spree/lib/cardlib.b')
-rw-r--r--appl/spree/lib/cardlib.b917
1 files changed, 917 insertions, 0 deletions
diff --git a/appl/spree/lib/cardlib.b b/appl/spree/lib/cardlib.b
new file mode 100644
index 00000000..67c4918b
--- /dev/null
+++ b/appl/spree/lib/cardlib.b
@@ -0,0 +1,917 @@
+implement Cardlib;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sets.m";
+ sets: Sets;
+ Set, set, A, B, All, None: import sets;
+include "../spree.m";
+ spree: Spree;
+ Attributes, Range, Object, Clique, Member, rand: import spree;
+include "objstore.m";
+ objstore: Objstore;
+include "cardlib.m";
+
+MAXPLAYERS: con 4;
+
+Layobject: adt {
+ lay: ref Object;
+ name: string;
+ packopts: int;
+ pick {
+ Obj =>
+ obj: ref Object; # nil if it's a frame
+ Frame =>
+ facing: int; # only valid if for frames
+ }
+};
+
+clique: ref Clique;
+cmembers: array of ref Cmember;
+cpids := array[8] of list of ref Cmember;
+
+# XXX first string is unnecessary as it's held in the Layobject anyway?
+layouts := array[17] of list of (string, ref Layout, ref Layobject);
+maxlayid := 1;
+cmemberid := 1;
+
+archiveobjs: array of list of (string, ref Object);
+
+defaultrank := array[13] of {12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+defaultsuitrank := array[] of {CLUBS => 0, DIAMONDS => 1, HEARTS => 2, SPADES => 3};
+
+table := array[] of {
+ 0 => array[] of {
+ (-1, dTOP|EXPAND, dBOTTOM, dTOP),
+ },
+ 1 => array [] of {
+ (0, dBOTTOM|FILLX, dBOTTOM, dTOP),
+ (-1, dTOP|EXPAND, dBOTTOM, dTOP),
+ },
+ 2 => array[] of {
+ (0, dBOTTOM|FILLX, dBOTTOM, dTOP),
+ (1, dTOP|FILLX, dTOP, dBOTTOM),
+ (-1, dTOP|EXPAND, dBOTTOM, dTOP)
+ },
+ 3 => array[] of {
+ (2, dRIGHT|FILLY, dRIGHT, dLEFT),
+ (0, dBOTTOM|FILLX, dBOTTOM, dTOP),
+ (1, dTOP|FILLX, dTOP, dBOTTOM),
+ (-1, dRIGHT|EXPAND, dBOTTOM, dTOP)
+ },
+ 4 => array[] of {
+ (1, dLEFT|FILLY, dLEFT, dRIGHT),
+ (3, dRIGHT|FILLY, dRIGHT, dLEFT),
+ (0, dBOTTOM|FILLX, dBOTTOM, dTOP),
+ (2, dTOP|FILLX, dTOP, dBOTTOM),
+ (-1, dRIGHT|EXPAND, dBOTTOM, dTOP)
+ },
+};
+
+
+init(mod: Spree, g: ref Clique)
+{
+ sys = load Sys Sys->PATH;
+ sets = load Sets Sets->PATH;
+ if (sets == nil)
+ panic(sys->sprint("cannot load %s: %r", Sets->PATH));
+ objstore = load Objstore Objstore->PATH;
+ if (objstore == nil)
+ panic(sys->sprint("cannot load %s: %r", Objstore->PATH));
+ objstore->init(mod, g);
+ clique = g;
+ spree = mod;
+}
+
+archive(): ref Object
+{
+ for (i := 0; i < len cmembers; i++) {
+ cp := cmembers[i];
+ setarchivename(cp.obj, "member" + string i);
+ setarchivename(cp.layout.lay, "layout" + string i);
+ sel := cp.sel;
+ if (sel.stack != nil)
+ setarchivename(sel.stack, "sel" + string i);
+ }
+ for (i = 0; i < len layouts; i++) {
+ for (ll := layouts[i]; ll != nil; ll = tl ll) {
+ (name, lay, layobj) := hd ll;
+ if (name != nil)
+ layobj.lay.setattr("layname", name, None);
+ pick l := layobj {
+ Frame =>
+ l.lay.setattr("facing", sides[l.facing], None);
+ Obj =>
+ setarchivename(l.obj, "layid" + l.obj.getattr("layid"));
+ }
+ }
+ }
+ # XXX should archive layouts that aren't particular to a member.
+ archiveobj := clique.newobject(nil, None, "archive");
+ setarchivename(archiveobj, "archive");
+ archiveobj.setattr("maxlayid", string maxlayid, None);
+ archiveobj.setattr("cmemberid", string cmemberid, None);
+ return archiveobj;
+}
+
+setarchivename(o: ref Object, name: string)
+{
+ objstore->setname(o, name);
+}
+
+getarchiveobj(name: string): ref Object
+{
+ return objstore->get(name);
+}
+
+archivearray(a: array of ref Object, name: string)
+{
+ for (i := 0; i < len a; i++)
+ objstore->setname(a[i], name + string i);
+}
+
+getarchivearray(name: string): array of ref Object
+{
+ l: list of ref Object;
+ for (i := 0; ; i++) {
+ o := objstore->get(name + string i);
+ if (o == nil)
+ break;
+ l = o :: l;
+ }
+ a := array[i] of ref Object;
+ for (; l != nil; l = tl l)
+ a[--i] = hd l;
+ return a;
+}
+
+unarchive(): ref Object
+{
+ objstore->unarchive();
+ archiveobj := getarchiveobj("archive");
+ cpl: list of ref Cmember;
+ for (i := 0; (o := getarchiveobj("member" + string i)) != nil; i++) {
+ cp := ref Cmember(
+ i,
+ int o.getattr("id"),
+ clique.membernamed(o.getattr("name")),
+ o,
+ ref Layout(getarchiveobj("layout" + string i)),
+ ref Selection(getarchiveobj("sel" + string i), -1, 1, (0, 0), nil)
+ );
+ cp.sel.ownerid = cp.id;
+ sel := cp.sel;
+ if (sel.stack != nil && (selstr := sel.stack.getattr("sel")) != nil) {
+ (n, val) := sys->tokenize(selstr, " ");
+ if (tl val != nil && hd tl val == "-")
+ (sel.r.start, sel.r.end) = (int hd val, int hd tl tl val);
+ else {
+ idxl: list of int;
+ sel.isrange = 0;
+ for (; val != nil; val = tl val)
+ idxl = int hd val :: idxl;
+ sel.idxl = idxl;
+ }
+ }
+ lay := cp.layout.lay;
+ # there should be exactly one child, of type "layframe"
+ if (len lay.children != 1 || lay.children[0].objtype != "layframe")
+ panic("invalid layout");
+ x := strhash(nil, len layouts);
+ layouts[x] = (nil, cp.layout, obj2layobj(lay.children[0])) :: layouts[x];
+ unarchivelayoutobj(cp.layout, lay.children[0]);
+ cpl = cp :: cpl;
+ }
+ cmembers = array[len cpl] of ref Cmember;
+ for (; cpl != nil; cpl = tl cpl) {
+ cp := hd cpl;
+ cmembers[cp.ord] = cp;
+ idx := cp.id % len cpids;
+ cpids[idx] = cp :: cpids[idx];
+ }
+
+ maxlayid = int archiveobj.getattr("maxlayid");
+ cmemberid = int archiveobj.getattr("cmemberid");
+ return archiveobj;
+}
+
+unarchivelayoutobj(layout: ref Layout, o: ref Object)
+{
+ for (i := 0; i < len o.children; i++) {
+ child := o.children[i];
+ layobj := obj2layobj(child);
+ if (layobj.name != nil) {
+ x := strhash(layobj.name, len layouts);
+ layouts[x] = (layobj.name, layout, layobj) :: layouts[x];
+ }
+ if (tagof(layobj) == tagof(Layobject.Frame))
+ unarchivelayoutobj(layout, child);
+ }
+}
+
+obj2layobj(o: ref Object): ref Layobject
+{
+ case o.objtype {
+ "layframe" =>
+ return ref Layobject.Frame(
+ o,
+ o.getattr("layname"),
+ s2packopts(o.getattr("opts")),
+ searchopt(sides, o.getattr("facing"))
+ );
+ "layobj" =>
+ return ref Layobject.Obj(
+ o,
+ o.getattr("layname"),
+ s2packopts(o.getattr("opts")),
+ getarchiveobj("layid" + o.getattr("layid"))
+ );
+ * =>
+ panic("invalid layobject found, of type '" + o.objtype + "'");
+ return nil;
+ }
+}
+
+Cmember.join(member: ref Member, ord: int): ref Cmember
+{
+ cmembers = (array[len cmembers + 1] of ref Cmember)[0:] = cmembers;
+ if (ord == -1)
+ ord = len cmembers - 1;
+ else {
+ cmembers[ord + 1:] = cmembers[ord:len cmembers - 1];
+ for (i := ord + 1; i < len cmembers; i++)
+ cmembers[i].ord = i;
+ }
+ cp := cmembers[ord] = ref Cmember(ord, cmemberid++, member, nil, nil, nil);
+ cp.obj = clique.newobject(nil, All, "member");
+ cp.obj.setattr("id", string cp.id, All);
+ cp.obj.setattr("name", member.name, All);
+ cp.obj.setattr("you", string cp.id, None.add(member.id));
+ cp.obj.setattr("cliquetitle", clique.fname, All);
+ cp.layout = newlayout(cp.obj, None.add(member.id));
+ cp.sel = ref Selection(nil, cp.id, 1, (0, 0), nil);
+
+ idx := cp.id % len cpids;
+ cpids[idx] = cp :: cpids[idx];
+ return cp;
+}
+
+Cmember.find(p: ref Member): ref Cmember
+{
+ id := p.id;
+ for (i := 0; i < len cmembers; i++)
+ if (cmembers[i].p.id == id)
+ return cmembers[i];
+ return nil;
+}
+
+Cmember.index(ord: int): ref Cmember
+{
+ if (ord < 0 || ord >= len cmembers)
+ return nil;
+ return cmembers[ord];
+}
+
+Cmember.next(cp: self ref Cmember, fwd: int): ref Cmember
+{
+ if (!fwd)
+ return cp.prev(1);
+ x := cp.ord + 1;
+ if (x >= len cmembers)
+ x = 0;
+ return cmembers[x];
+}
+
+Cmember.prev(cp: self ref Cmember, fwd: int): ref Cmember
+{
+ if (!fwd)
+ return cp.next(1);
+ x := cp.ord - 1;
+ if (x < 0)
+ x = len cmembers - 1;
+ return cmembers[x];
+}
+
+Cmember.leave(cp: self ref Cmember)
+{
+ ord := cp.ord;
+ cmembers[ord] = nil;
+ cmembers[ord:] = cmembers[ord + 1:];
+ cmembers[len cmembers - 1] = nil;
+ cmembers = cmembers[0:len cmembers - 1];
+ for (i := ord; i < len cmembers; i++)
+ cmembers[i].ord = i;
+ cp.obj.delete();
+ dellayout(cp.layout);
+ cp.layout = nil;
+ idx := cp.id % len cpids;
+ l: list of ref Cmember;
+ ll := cpids[idx];
+ for (; ll != nil; ll = tl ll)
+ if (hd ll != cp)
+ l = hd ll :: l;
+ cpids[idx] = l;
+ cp.ord = -1;
+}
+
+Cmember.findid(id: int): ref Cmember
+{
+ for (l := cpids[id % len cpids]; l != nil; l = tl l)
+ if ((hd l).id == id)
+ return hd l;
+ return nil;
+}
+
+newstack(parent: ref Object, owner: ref Member, spec: Stackspec): ref Object
+{
+ vis := All;
+ if (spec.conceal) {
+ vis = None;
+ if (owner != nil)
+ vis = vis.add(owner.id);
+ }
+ o := clique.newobject(parent, vis, "stack");
+ o.setattr("maxcards", string spec.maxcards, All);
+ o.setattr("style", spec.style, All);
+
+ # XXX provide some means for this to contain the member's name?
+ o.setattr("title", spec.title, All);
+ return o;
+}
+
+makecard(deck: ref Object, c: Card, rear: string): ref Object
+{
+ card := clique.newobject(deck, None, "card");
+ card.setattr("face", string c.face, All);
+ vis := None;
+ if(c.face)
+ vis = All;
+ card.setattr("number", string (c.number * 4 + c.suit), vis);
+ if (rear != nil)
+ card.setattr("rear", rear, All);
+ return card;
+}
+
+makecards(deck: ref Object, r: Range, rear: string)
+{
+ for (i := r.start; i < r.end; i++)
+ for(suit := 0; suit < 4; suit++)
+ makecard(deck, (suit, i, 0), rear);
+}
+
+# deal n cards to each member, if possible.
+# deal in chunks for efficiency.
+# if accuracy is required (e.g. dealing from an unshuffled
+# deck containing known cards) then this'll have to change.
+deal(deck: ref Object, n: int, stacks: array of ref Object, first: int)
+{
+ ncards := len deck.children;
+ ord := 0;
+ permember := n;
+ leftover := 0;
+ if (n * len stacks > ncards) {
+ # if trying to deal more cards than we've got,
+ # deal all that we've got, distributing the remainder fairly.
+ permember = ncards / len stacks;
+ leftover = ncards % len stacks;
+ }
+ for (i := 0; i < len stacks; i++) {
+ n = permember;
+ if (leftover > 0) {
+ n++;
+ leftover--;
+ }
+ priv := stacks[(first + i) % len stacks];
+ deck.transfer((ncards - n, ncards), priv, len priv.children);
+ priv.setattr("n", string (int priv.getattr("n") + n), All);
+ # make cards visible to member
+ for (j := len priv.children - n; j < len priv.children; j++)
+ setface(priv.children[j], 1);
+
+ ncards -= n;
+ }
+}
+
+setface(card: ref Object, face: int)
+{
+ # XXX check parent stack style and if it's a pile,
+ # only expose a face up card at the top.
+
+ card.setattr("face", string face, All);
+ if (face)
+ card.setattrvisibility("number", All);
+ else
+ card.setattrvisibility("number", None);
+}
+
+nmembers(): int
+{
+ return len cmembers;
+}
+
+getcard(card: ref Object): Card
+{
+ n := int card.getattr("number");
+ (suit, num) := (n % 4, n / 4);
+ return Card(suit, num, int card.getattr("face"));
+}
+
+getcards(stack: ref Object): array of Card
+{
+ a := array[len stack.children] of Card;
+ for (i := 0; i < len a; i++)
+ a[i] = getcard(stack.children[i]);
+ return a;
+}
+
+discard(stk, pile: ref Object, facedown: int)
+{
+ n := len stk.children;
+ if (facedown)
+ for (i := 0; i < n; i++)
+ setface(stk.children[i], 0);
+ stk.transfer((0, n), pile, len pile.children);
+}
+
+# shuffle children into a random order. first we make all the children
+# invisible (which will cause them to be deleted in the clients) then
+# shuffle to our heart's content, and make visible again...
+shuffle(o: ref Object)
+{
+ ovis := o.visibility;
+ o.setvisibility(None);
+ a := o.children;
+ n := len a;
+ for (i := 0; i < n; i++) {
+ j := i + rand(n - i);
+ (a[i], a[j]) = (a[j], a[i]);
+ }
+ o.setvisibility(ovis);
+}
+
+sort(o: ref Object, rank, suitrank: array of int)
+{
+ if (rank == nil)
+ rank = defaultrank;
+ if (suitrank == nil)
+ suitrank = defaultsuitrank;
+ ovis := o.visibility;
+ o.setvisibility(None);
+ cardmergesort(o.children, array[len o.children] of ref Object, rank, suitrank);
+ o.setvisibility(ovis);
+}
+
+cardcmp(a, b: ref Object, rank, suitrank: array of int): int
+{
+ c1 := getcard(a);
+ c2 := getcard(b);
+ if (suitrank[c1.suit] != suitrank[c2.suit])
+ return suitrank[c1.suit] - suitrank[c2.suit];
+ return rank[c1.number] - rank[c2.number];
+}
+
+cardmergesort(a, b: array of ref Object, rank, suitrank: array of int)
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ cardmergesort(a[0:m], b[0:m], rank, suitrank);
+ cardmergesort(a[m:], b[m:], rank, suitrank);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if (cardcmp(b[i], b[j], rank, suitrank) > 0)
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if (i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
+
+# reverse and flip all cards in stack.
+flip(stack: ref Object)
+{
+ ovis := stack.visibility;
+ stack.setvisibility(None);
+ a := stack.children;
+ (n, m) := (len a, len a / 2);
+ for (i := 0; i < m; i++) {
+ j := n - i - 1;
+ (a[i], a[j]) = (a[j], a[i]);
+ }
+ for (i = 0; i < n; i++)
+ setface(a[i], !int a[i].getattr("face"));
+ stack.setvisibility(ovis);
+}
+
+selection(stack: ref Object): ref Selection
+{
+ if ((owner := stack.getattr("owner")) != nil &&
+ (cp := Cmember.findid(int owner)) != nil)
+ return cp.sel;
+ return nil;
+}
+
+Selection.set(sel: self ref Selection, stack: ref Object)
+{
+ if (stack == sel.stack)
+ return;
+ if (stack != nil) {
+ oldowner := stack.getattr("owner");
+ if (oldowner != nil) {
+ oldcp := Cmember.findid(int oldowner);
+ if (oldcp != nil)
+ oldcp.sel.set(nil);
+ }
+ }
+ if (sel.stack != nil)
+ sel.stack.setattr("owner", nil, All);
+ sel.stack = stack;
+ sel.isrange = 1;
+ sel.r = (0, 0);
+ sel.idxl = nil;
+ setsel(sel);
+}
+
+Selection.setexcl(sel: self ref Selection, stack: ref Object): int
+{
+ if (stack != nil && (oldowner := stack.getattr("owner")) != nil)
+ if ((cp := Cmember.findid(int oldowner)) != nil && !cp.sel.isempty())
+ return 0;
+ sel.set(stack);
+ return 1;
+}
+
+Selection.owner(sel: self ref Selection): ref Cmember
+{
+ return Cmember.findid(sel.ownerid);
+}
+
+Selection.setrange(sel: self ref Selection, r: Range)
+{
+ if (!sel.isrange) {
+ sel.idxl = nil;
+ sel.isrange = 1;
+ }
+ sel.r = r;
+ setsel(sel);
+}
+
+Selection.addindex(sel: self ref Selection, i: int)
+{
+ if (sel.isrange) {
+ sel.r = (0, 0);
+ sel.isrange = 0;
+ }
+ ll: list of int;
+ for (l := sel.idxl; l != nil; l = tl l) {
+ if (hd l >= i)
+ break;
+ ll = hd l :: ll;
+ }
+ if (l != nil && hd l == i)
+ return;
+ l = i :: l;
+ for (; ll != nil; ll = tl ll)
+ l = hd ll :: l;
+ sel.idxl = l;
+ setsel(sel);
+}
+
+Selection.delindex(sel: self ref Selection, i: int)
+{
+ if (sel.isrange) {
+ sys->print("cardlib: delindex from range-type selection\n");
+ return;
+ }
+ ll: list of int;
+ for (l := sel.idxl; l != nil; l = tl l) {
+ if (hd l == i) {
+ l = tl l;
+ break;
+ }
+ ll = hd l :: ll;
+ }
+ for (; ll != nil; ll = tl ll)
+ l = hd ll :: l;
+ sel.idxl = l;
+ setsel(sel);
+}
+
+Selection.isempty(sel: self ref Selection): int
+{
+ if (sel.stack == nil)
+ return 1;
+ if (sel.isrange)
+ return sel.r.start == sel.r.end;
+ return sel.idxl == nil;
+}
+
+Selection.isset(sel: self ref Selection, index: int): int
+{
+ if (sel.isrange)
+ return index >= sel.r.start && index < sel.r.end;
+ for (l := sel.idxl; l != nil; l = tl l)
+ if (hd l == index)
+ return 1;
+ return 0;
+}
+
+Selection.transfer(sel: self ref Selection, dst: ref Object, index: int)
+{
+ if (sel.isempty())
+ return;
+ src := sel.stack;
+ if (sel.isrange) {
+ r := sel.r;
+ sel.set(nil);
+ src.transfer(r, dst, index);
+ } else {
+ if (sel.stack == dst) {
+ sys->print("cardlib: cannot move multisel to same stack\n");
+ return;
+ }
+ xl := l := sel.idxl;
+ sel.set(nil);
+ rl: list of Range;
+ for (; l != nil; l = tl l) {
+ r := Range(hd l, hd l);
+ last := l;
+ # concatenate adjacent items, for efficiency.
+ for (l = tl l; l != nil; (last, l) = (l, tl l)) {
+ if (hd l != r.end + 1)
+ break;
+ r.end = hd l;
+ }
+ rl = (r.start, r.end + 1) :: rl;
+ l = last;
+ }
+ # do ranges in reverse, so that later ranges
+ # aren't affected by earlier ones.
+ if (index == -1)
+ index = len dst.children;
+ for (; rl != nil; rl = tl rl)
+ src.transfer(hd rl, dst, index);
+ }
+}
+
+setsel(sel: ref Selection)
+{
+ if (sel.stack == nil)
+ return;
+ s := "";
+ if (sel.isrange) {
+ if (sel.r.end > sel.r.start)
+ s = string sel.r.start + " - " + string sel.r.end;
+ } else {
+ if (sel.idxl != nil) {
+ s = string hd sel.idxl;
+ for (l := tl sel.idxl; l != nil; l = tl l)
+ s += " " + string hd l;
+ }
+ }
+ if (s != nil)
+ sel.stack.setattr("owner", string sel.owner().id, All);
+ else
+ sel.stack.setattr("owner", nil, All);
+ vis := None.add(sel.owner().p.id);
+ sel.stack.setattr("sel", s, vis);
+ sel.stack.setattrvisibility("sel", vis);
+}
+
+newlayout(parent: ref Object, vis: Set): ref Layout
+{
+ l := ref Layout(clique.newobject(parent, vis, "layout"));
+ x := strhash(nil, len layouts);
+ layobj := ref Layobject.Frame(nil, "", dTOP|EXPAND|FILLX|FILLY, dTOP);
+ layobj.lay = clique.newobject(l.lay, All, "layframe");
+ layobj.lay.setattr("opts", packopts2s(layobj.packopts), All);
+ layouts[x] = (nil, l, layobj) :: layouts[x];
+# sys->print("[%d] => ('%s', %ux, %ux) (new layout)\n", x, "", l, layobj);
+ return l;
+}
+
+addlayframe(name, parent: string, layout: ref Layout, packopts: int, facing: int)
+{
+# sys->print("addlayframe('%s', %ux, name: %s\n", parent, layout, name);
+ addlay(parent, layout, ref Layobject.Frame(nil, name, packopts, facing));
+}
+
+addlayobj(name, parent: string, layout: ref Layout, packopts: int, obj: ref Object)
+{
+# sys->print("addlayobj('%s', %ux, name: %s, obj %d\n", parent, layout, name, obj.id);
+ addlay(parent, layout, ref Layobject.Obj(nil, name, packopts, obj));
+}
+
+addlay(parent: string, layout: ref Layout, layobj: ref Layobject)
+{
+ a := layouts;
+ name := layobj.name;
+ x := strhash(name, len a);
+ added := 0;
+ for (nl := a[strhash(parent, len a)]; nl != nil; nl = tl nl) {
+ (s, lay, parentlay) := hd nl;
+ if (s == parent && (layout == nil || layout == lay)) {
+ pick p := parentlay {
+ Obj =>
+ sys->fprint(sys->fildes(2),
+ "cardlib: cannot add layout to non-frame: %d\n", p.obj.id);
+ Frame =>
+ nlayobj := copylayobj(layobj);
+ nlayobj.packopts = packoptsfacing(nlayobj.packopts, p.facing);
+ o: ref Object;
+ pick lo := nlayobj {
+ Obj =>
+ o = clique.newobject(p.lay, All, "layobj");
+ id := lo.obj.getattr("layid");
+ if (id == nil) {
+ id = string maxlayid++;
+ lo.obj.setattr("layid", id, All);
+ }
+ o.setattr("layid", id, All);
+ Frame =>
+ o = clique.newobject(p.lay, All, "layframe");
+ lo.facing = (lo.facing + p.facing) % 4;
+ }
+ o.setattr("opts", packopts2s(nlayobj.packopts), All);
+ nlayobj.lay = o;
+ if (name != nil)
+ a[x] = (name, lay, nlayobj) :: a[x];
+ added++;
+ }
+ }
+ }
+ if (added == 0)
+ sys->print("no parent found, adding '%s', parent '%s', layout %ux\n",
+ layobj.name, parent, layout);
+# sys->print("%d new entries\n", added);
+}
+
+maketable(parent: string)
+{
+ # make a table for all current members.
+ plcount := len cmembers;
+ packopts := table[plcount];
+ for (i := 0; i < plcount; i++) {
+ layout := cmembers[i].layout;
+ for (j := 0; j < len packopts; j++) {
+ (ord, outer, inner, facing) := packopts[j];
+ name := "public";
+ if (ord != -1)
+ name = "p" + string ((ord + i) % plcount);
+ addlayframe("@" + name, parent, layout, outer, dTOP);
+ addlayframe(name, "@" + name, layout, inner, facing);
+ }
+ }
+}
+
+dellay(name: string, layout: ref Layout)
+{
+ a := layouts;
+ x := strhash(name, len a);
+ rl: list of (string, ref Layout, ref Layobject);
+ for (nl := a[x]; nl != nil; nl = tl nl) {
+ (s, lay, layobj) := hd nl;
+ if (s != name || (layout != nil && layout != lay))
+ rl = hd nl :: rl;
+ }
+ a[x] = rl;
+}
+
+dellayout(layout: ref Layout)
+{
+ for (i := 0; i < len layouts; i++) {
+ ll: list of (string, ref Layout, ref Layobject);
+ for (nl := layouts[i]; nl != nil; nl = tl nl) {
+ (s, lay, layobj) := hd nl;
+ if (lay != layout)
+ ll = hd nl :: ll;
+ }
+ layouts[i] = ll;
+ }
+}
+
+copylayobj(obj: ref Layobject): ref Layobject
+{
+ pick o := obj {
+ Frame =>
+ return ref *o;
+ Obj =>
+ return ref *o;
+ }
+ return nil;
+}
+
+packoptsfacing(opts, facing: int): int
+{
+ if (facing == dTOP)
+ return opts;
+ nopts := 0;
+
+ # 4 directions
+ nopts |= (facing + (opts & dMASK)) % 4;
+
+ # 2 orientations
+ nopts |= ((facing + ((opts & oMASK) >> oSHIFT)) % 4) << oSHIFT;
+
+ # 8 anchorpoints (+ centre)
+ a := (opts & aMASK);
+ if (a != aCENTRE)
+ a = ((((a >> aSHIFT) - 1 + facing * 2) % 8) + 1) << aSHIFT;
+ nopts |= a;
+
+ # two fill options
+ if (facing % 2) {
+ if (opts & FILLX)
+ nopts |= FILLY;
+ if (opts & FILLY)
+ nopts |= FILLX;
+ } else
+ nopts |= (opts & (FILLX | FILLY));
+
+ nopts |= (opts & EXPAND);
+ return nopts;
+}
+
+# these arrays are dependent on the ordering of
+# the relevant constants defined in cardlib.m
+
+sides := array[] of {"top", "left", "bottom", "right"};
+anchors := array[] of {"centre", "n", "nw", "w", "sw", "s", "se", "e", "ne"};
+orientations := array[] of {"right", "up", "left", "down"};
+fills := array[] of {"none", "x", "y", "both"};
+
+packopts2s(opts: int): string
+{
+ s := orientations[(opts & oMASK) >> oSHIFT] +
+ " -side " + sides[opts & dMASK];
+ if ((opts & aMASK) != aCENTRE)
+ s += " -anchor " + anchors[(opts & aMASK) >> aSHIFT];
+ if (opts & EXPAND)
+ s += " -expand 1";
+ if (opts & (FILLX | FILLY))
+ s += " -fill " + fills[(opts & FILLMASK) >> FILLSHIFT];
+ return s;
+}
+
+searchopt(a: array of string, s: string): int
+{
+ for (i := 0; i < len a; i++)
+ if (a[i] == s)
+ return i;
+ panic("unknown pack option '" + s + "'");
+ return 0;
+}
+
+s2packopts(s: string): int
+{
+ (nil, toks) := sys->tokenize(s, " ");
+ if (toks == nil)
+ panic("invalid packopts: " + s);
+ p := searchopt(orientations, hd toks) << oSHIFT;
+ for (toks = tl toks; toks != nil; toks = tl tl toks) {
+ if (tl toks == nil)
+ panic("invalid packopts: " + s);
+ arg := hd tl toks;
+ case hd toks {
+ "-anchor" =>
+ p |= searchopt(anchors, arg) << aSHIFT;
+ "-fill" =>
+ p |= searchopt(fills, arg) << FILLSHIFT;
+ "-side" =>
+ p |= searchopt(sides, arg) << dSHIFT;
+ "-expand" =>
+ if (int hd tl toks)
+ p |= EXPAND;
+ * =>
+ panic("unknown pack option: " + hd toks);
+ }
+ }
+ return p;
+}
+
+panic(e: string)
+{
+ sys->fprint(sys->fildes(2), "cardlib panic: %s\n", e);
+ raise "panic";
+}
+
+assert(b: int, err: string)
+{
+ if (b == 0)
+ raise "parse:" + err;
+}
+
+# from Aho Hopcroft Ullman
+strhash(s: string, n: int): int
+{
+ h := 0;
+ m := len s;
+ for(i := 0; i<m; i++){
+ h = 65599 * h + s[i];
+ }
+ return (h & 16r7fffffff) % n;
+}