WhiteboardDBPrivateImpl.mesa
Copyright (C) 1984 by Xerox Corporation. All rights reserved.
Last edited by
Donahue, April 10, 1986 2:40:16 pm PST
(Changed the implementation so that this interface never catches any errors -- they will all be caught at the WhiteboardDB level and turned into WBErrors)
(Added Grid size property to whiteboards)
(Added create date property)
Last Edited by: Winkler, December 18, 1984 10:50:36 am PST
DIRECTORY
AlpTransaction,
Atom,
BasicTime,
DB,
DBCommon,
DBDefs,
FileNames USING[GetShortName],
IO USING [PutR, card],
Rope,
TextFind,
ViewerTools,
WhiteboardDBPrivate;
WhiteboardDBPrivateImpl: CEDAR PROGRAM
IMPORTS BasicTime, DB, FileNames, Atom, IO, Rope, TextFind, AlpTransaction
EXPORTS WhiteboardDBPrivate =
BEGIN OPEN WhiteboardDBPrivate, DBDefs;
ROPE: TYPE = Rope.ROPE;
WBSegment: PUBLIC ROPENIL;
readOnly: PUBLIC BOOLTRUE;
wbType: PUBLIC ATOM ← $Whiteboard;
whiteboardTrans: DBCommon.TransactionHandle;
volumeGroupID: AlpTransaction.VolumeGroupID;
Database schema and initialization
WBEntity: DBDefs.Domain; -- the domain of whiteboards
WBTypeProc: PROC[] RETURNS[type: TypeCode] = { type ← DB.TypeForDomain[WBEntity] };
WBType: DB.TypeSpec = [indirect[WBTypeProc]];
Each whiteboard has a version property (which is changed whenever the contents of the whiteboard changes) and a "grid size" property
versionProp: CARDINAL = 1;
gridProp: CARDINAL = 2;
createDateProp: CARDINAL = 3;
WhiteboardPropFields: DB.FieldSpec = DB.L2FS[LIST[[name: "whiteboard", type: WBType], [name: "version", type: DB.Integer], [name: "grid", type: DB.Integer], [name: "createDate", type: DB.Time]]];
WhiteboardProps: DBDefs.Relation;
WBItem: DBDefs.Domain; -- the domain of entities that are displayed on whiteboards
Note: DBDefs.Domain; -- notes (text boxes) are a subclass of WBItems
Icon: DBDefs.Domain; -- Icons are the other class of WBItems
WBItemTypeProc: PROC[] RETURNS[type: TypeCode] = {type ← DB.TypeForDomain[WBItem]};
WBItemType: DB.TypeSpec = [indirect[WBItemTypeProc]];
NoteTypeProc: PROC[] RETURNS[type: TypeCode] = { type ← DB.TypeForDomain[Note] };
NoteType: DB.TypeSpec = [indirect[NoteTypeProc]];
IconTypeProc: PROC[] RETURNS[type: TypeCode] = { type ← DB.TypeForDomain[Icon] };
IconType: DB.TypeSpec = [indirect[IconTypeProc]];
A Note has attributes: [contents: ROPE, format: ROPE]
contents: CARDINAL = 1;
format: CARDINAL = 2;
NotePropFields: DB.FieldSpec = DB.L2FS[LIST[[name: "note", type: NoteType], [name: "contents", type: DB.String, lengthHint: 50], [name: "format", type: DB.String, lengthHint: 50]]];
NoteProps: DBDefs.Relation;
An Icon has attributes: [iconName: ROPE, iconLabel: ROPE, iconType: ROPE, iconIcon: ROPE, iconArgument: ROPE -- this is for tools only]
iconName: CARDINAL = 1;
iconLabel: CARDINAL = 2;
iconType: CARDINAL = 3;
iconIcon: CARDINAL = 4;
iconArgument: CARDINAL = 5;
IconPropFields: DB.FieldSpec = DB.L2FS[LIST[[name: "icon", type: IconType], [name: "iconName", type: DB.String, lengthHint: 40], [name: "iconLabel", type: DB.String, lengthHint: 40], [name: "iconType", type: DB.String, lengthHint: 20], [name: "iconIcon", type: DB.String, lengthHint: 20], [name: "iconArgument", type: DB.String, lengthHint: 40]]];
IconProps: DBDefs.Relation;
Each WBItem also has the additional properties [wb: WBEntity, x, y, w, h: INT]
container: DBDefs.Relation;
containerItem: CARDINAL = 0;
containerWB: CARDINAL = 1;
containerX: CARDINAL = 2;
containerY: CARDINAL = 3;
containerW: CARDINAL = 4;
containerH: CARDINAL = 5;
containerPropFields: DB.FieldSpec = DB.L2FS[LIST[[name: "item", type: WBItemType], [name: "of", type: WBType], [name: "x", type: DB.Integer], [name: "y", type: DB.Integer], [name: "w", type: DB.Integer], [name: "h", type: DB.Integer]]];
CloseTransaction: PUBLIC PROC [] = {
aborted: BOOLFALSE;
IF whiteboardTrans = NIL THEN RETURN;
DB.CloseTransaction[whiteboardTrans !
DB.Aborted => { aborted ← TRUE; CONTINUE };
DB.Error, DB.Failure => CONTINUE ];
IF aborted THEN DB.AbortTransaction[whiteboardTrans];
whiteboardTrans ← NIL };
OpenTransaction: PUBLIC PROC [] = {
schemaInvalid: BOOL;
IF whiteboardTrans = NIL THEN {
[whiteboardTrans, schemaInvalid] ← DB.OpenTransaction[$Whiteboard];
IF schemaInvalid THEN ResetSchema[] };
volumeGroupID ← AlpTransaction.GetNextVolumeGroup[handle: NARROW[whiteboardTrans.trans]] };
AbortTransaction: PUBLIC PROC [] = {
IF whiteboardTrans = NIL THEN RETURN;
DB.AbortTransaction[whiteboardTrans ! DB.Error, DB.Failure, DB.Aborted => CONTINUE ];
whiteboardTrans ← NIL };
MarkTransaction: PUBLIC PROC [unlock: BOOLTRUE] = {
IF whiteboardTrans # NIL THEN {
alpTrans: AlpTransaction.Handle = NARROW[whiteboardTrans.trans];
DB.MarkTransaction[whiteboardTrans];
IF unlock THEN AlpTransaction.UnlockOwnerDB[alpTrans, volumeGroupID] } };
Initialize: PUBLIC PROC[DBFile: ROPE] RETURNS[serverDown: BOOL] =
BEGIN
serverDown ← FALSE;
IF NOT Rope.Equal[WBSegment, DBFile, FALSE] THEN {
ENABLE DB.Failure => {WBSegment ← NIL; serverDown ← TRUE; CONTINUE};
WBSegment ← DBFile;
CloseTransaction[];
SetUpSegment[] };
END;
ResetSchema: PROC[] = {
Note ← DB.DeclareDomain["Note", $Whiteboard];
Icon ← DB.DeclareDomain["WBIcon", $Whiteboard];
WBItem ← DB.DeclareDomain["WBItems", $Whiteboard];
DB.DeclareSubType[of: WBItem, is: Note];
DB.DeclareSubType[of: WBItem, is: Icon];
WBEntity ← DB.DeclareDomain["Whiteboard", $Whiteboard];
WhiteboardProps ← DB.DeclareProperty[name: "WhiteboardProps", segment: $Whiteboard, fields: WhiteboardPropFields];
NoteProps ← DB.DeclareProperty[name: "NoteProps", segment: $Whiteboard, fields: NotePropFields];
IconProps ← DB.DeclareProperty[name: "IconProps", segment: $Whiteboard, fields: IconPropFields];
container ← DB.DeclareProperty[name: "container", segment: $Whiteboard, fields: containerPropFields] };
SetUpSegment: PROC [] ~ {
segmentNumber: NAT = 310B;
readOnly ← FALSE;
DB.Initialize[nCachePages: 256];
try opening the segment for writing
DB.DeclareSegment[WBSegment, wbType, segmentNumber];
OpenTransaction[ ! DB.Error =>
IF code = ProtectionViolation THEN { readOnly ← TRUE; CONTINUE } ELSE REJECT ];
IF readOnly THEN {
attempt to open for writing failed; open it for reading only
CloseTransaction[];
DB.DeclareSegment[filePath: WBSegment, segment: wbType, number: segmentNumber, readonly: TRUE];
OpenTransaction[] } };
Operations on Whiteboards.
DeclareWhiteboard: PUBLIC PROC[name: ROPE] RETURNS[wb: Whiteboard] = {
wb ← DB.LookupEntity[WBEntity, name];
IF wb # NIL THEN RETURN;
wb ← DB.DeclareEntity[WBEntity, name];
[] ← DB.CreateRelship[WhiteboardProps, DB.L2VS[LIST[DB.E2V[wb], DB.I2V[0], DB.I2V[1], DB.T2V[BasicTime.Now[]]]]] };
GetVersion: PUBLIC PROC[wb: Whiteboard] RETURNS[version: INT] = {
version ← DB.V2I[DB.GetP[wb, WhiteboardProps, versionProp]] };
SetVersion: PUBLIC PROC[wb: Whiteboard, version: INT] = {
[] ← DB.SetP[wb, WhiteboardProps, versionProp, DB.I2V[version]] };
GetGridSize: PUBLIC PROC[wb: Whiteboard] RETURNS[gridSize: INT] = {
gridSize ← DB.V2I[DB.GetP[wb, WhiteboardProps, gridProp]];
IF gridSize <= 0 THEN gridSize ← 1 };
SetGridSize: PUBLIC PROC[wb: Whiteboard, gridSize: INT] = {
[] ← DB.SetP[wb, WhiteboardProps, gridProp, DB.I2V[gridSize]] };
GetCreateDate: PUBLIC PROC[wb: Whiteboard] RETURNS[date: BasicTime.GMT] = {
date ← DB.V2T[DB.GetP[wb, WhiteboardProps, createDateProp]] };
SetCreateDate: PUBLIC PROC[wb: Whiteboard, date: BasicTime.GMT] = {
[] ← DB.SetP[wb, WhiteboardProps, createDateProp, DB.T2V[date]] };
NameOf: PUBLIC PROC[wb: Whiteboard] RETURNS[name: ROPE] = {
name ← DB.EntityInfo[wb].name };
DestroyWhiteboard: PUBLIC PROC[wb: Whiteboard] = {
IF DB.NullEntity[wb] THEN RETURN;
DB.DestroyEntity[wb];
destroy all internal references
FOR wbList: LIST OF Whiteboard ← Enumerate[NIL], wbList.rest UNTIL wbList = NIL DO
nextWB: Whiteboard = wbList.first;
name: ROPE = NameOf[nextWB];
children: ChildSet = Children[nextWB, TRUE];
FOR child: WhiteboardDBPrivate.WBItem ← NextChild[children], NextChild[children] UNTIL child = NIL DO
IF Rope.Equal[name, GetIconProps[child].name] THEN DB.DestroyEntity[child]
ENDLOOP
ENDLOOP };
Exists: PUBLIC PROC[name: ROPE] RETURNS[BOOL] = {
RETURN[NOT DB.NullEntity[DB.LookupEntity[WBEntity, name]]] };
Enumerate: PUBLIC PROC[pattern: ROPE] RETURNS[wbList: LIST OF Whiteboard] = {
wbSet: DBDefs.EntitySet = DB.DomainSubset[d: WBEntity, lowName: NIL, highName: NIL, start: First];
BEGIN
ENABLE UNWIND => DB.ReleaseEntitySet[wbSet];
finder: TextFind.Finder = IF pattern = NIL THEN NIL ELSE TextFind.CreateFromRope[pattern: pattern, ignoreCase: TRUE, addBounds: TRUE];
FOR wb: DBDefs.Entity ← DB.NextEntity[wbSet], DB.NextEntity[wbSet] UNTIL wb = NIL DO
IF finder = NIL OR TextFind.SearchRope[finder, DB.EntityInfo[wb].name].found THEN
wbList ← CONS[wb, wbList]
ENDLOOP;
DB.ReleaseEntitySet[wbSet]
END };
Generic operations on Whiteboard Items.
FetchWBItem: PUBLIC PROC[name: ROPE, type: ItemType] RETURNS[item: WhiteboardDBPrivate.WBItem] = {
item ← IF type = note THEN DB.LookupEntity[Note, name] ELSE DB.LookupEntity[Icon, name] };
Parent: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem] RETURNS[wb: Whiteboard] = {
wb ← DB.V2E[DB.GetP[e: item, r: container, field: containerWB]] };
Children: PUBLIC PROC[wb: Whiteboard, onlyWhiteboards: BOOLFALSE] RETURNS[children: ChildSet] = {
rs: DBDefs.RelshipSet = DB.RelshipsWithEntityField[container, containerWB, wb];
children ← NEW[ChildSetObject ← [rs, onlyWhiteboards]] };
NextChild: PUBLIC PROC[children: ChildSet] RETURNS[child: WhiteboardDBPrivate.WBItem] = {
rs: DBDefs.RelshipSet = children.rs;
IF rs # NIL THEN {
IF NOT children.screen THEN {
next: DBDefs.Relship = DB.NextRelship[rs];
IF next = NIL THEN RETURN[NIL];
child ← DB.V2E[DB.GetF[next, containerItem]] }
ELSE
FOR next: DBDefs.Relship ← DB.NextRelship[rs], DB.NextRelship[rs] UNTIL next = NIL DO
child ← DB.V2E[DB.GetF[next, containerItem]];
IF TypeOf[child] = icon AND GetIconProps[child].type = wbType THEN EXIT
ELSE child ← NIL
ENDLOOP;
IF child = NIL THEN {DB.ReleaseRelshipSet[rs]; children.rs ← NIL} }
ELSE child ← NIL };
TypeOf: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem] RETURNS[type: ItemType] = {
DB.ToUnderType[item];
type ← IF DB.DomainEq[DB.EntityInfo[item].domain, Note] THEN note ELSE icon };
Position: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem] RETURNS[x, y: INT] = {
cRS: DBDefs.Relship = DB.LookupProperty[container, item];
x ← DB.V2I[DB.GetF[cRS, containerX]];
y ← DB.V2I[DB.GetF[cRS, containerY]] };
Size: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem] RETURNS[w, h: INT] = {
cRS: DBDefs.Relship = DB.LookupProperty[container, item];
w ← DB.V2I[DB.GetF[cRS, containerW]];
h ← DB.V2I[DB.GetF[cRS, containerH]] };
Move: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem, newX, newY: INT] = {
cRS: DBDefs.Relship = DB.LookupProperty[container, item];
DB.SetF[cRS, containerX, DB.I2V[newX]];
DB.SetF[cRS, containerY, DB.I2V[newY]] };
Destroy: PUBLIC PROC[item: WhiteboardDBPrivate.WBItem] = {
IF NOT DB.NullEntity[item] THEN DB.DestroyEntity[item] };
Operations on Whiteboard Notes.
MakeRandomName: PROC[] RETURNS[name: Rope.ROPE] = {
random: LONG CARDINAL = LOOPHOLE[BasicTime.GetClockPulses[]];
name ← IO.PutR[IO.card[random]] };
NewNote: PUBLIC PROC[parent: Whiteboard, x, y, w, h: INT, content: ViewerTools.TiogaContents] RETURNS[newNote: WBNote] = {
note: WBNote = DB.DeclareEntity[d: Note, name: MakeRandomName[]];
props: DBDefs.Relship = DB.CreateRelship[container, DB.L2VS[LIST[DB.E2V[note], DB.E2V[parent], DB.I2V[x], DB.I2V[y], DB.I2V[w], DB.I2V[h]]]];
noteProps: DBDefs.Relship = DB.CreateRelship[NoteProps, DB.L2VS[LIST[DB.E2V[note], DB.S2V[content.contents], DB.S2V[content.formatting]]]];
RETURN[note] };
GetContents: PUBLIC PROC[note: WBNote] RETURNS[content: ViewerTools.TiogaContents] = {
content ← NEW[ViewerTools.TiogaContentsRec ← []];
content.contents ← DB.V2S[DB.GetP[note, NoteProps, contents]];
content.formatting ← DB.V2S[DB.GetP[note, NoteProps, format]] };
SetContents: PUBLIC PROC[note: WBNote, content: ViewerTools.TiogaContents] = {
DB.SetP[note, NoteProps, contents, DB.S2V[content.contents]];
DB.SetP[note, NoteProps, format, DB.S2V[content.formatting]] };
Grow: PUBLIC PROC[note: WBNote, newW, newH: INT] = {
DB.SetP[note, container, containerW, DB.I2V[newW]];
DB.SetP[note, container, containerH, DB.I2V[newH]] };
Operations on Whiteboard Icons.
NewIcon: PUBLIC PROC[parent: Whiteboard, x, y: INT, name, label, icon, argument: ROPE, type: ATOM] RETURNS[newIcon: WBIcon] = {
wbIcon: WhiteboardDBPrivate.WBIcon = DB.DeclareEntity[d: Icon, name: MakeRandomName[]];
props: DBDefs.Relship = DB.CreateRelship[container, DB.L2VS[LIST[DB.E2V[wbIcon], DB.E2V[parent], DB.I2V[x], DB.I2V[y], DB.I2V[64], DB.I2V[64]]]];
iconProps: DBDefs.Relship = DB.CreateRelship[IconProps, DB.L2VS[LIST[DB.E2V[wbIcon], DB.S2V[name], DB.S2V[label], DB.S2V[Atom.GetPName[type]], DB.S2V[icon], DB.S2V[argument]]]];
RETURN[wbIcon] };
GetDisplayProps: PUBLIC PROC[wbIcon: WBIcon] RETURNS[label, icon: ROPE] = {
label ← DB.V2S[DB.GetP[wbIcon, IconProps, iconLabel]];
icon ← DB.V2S[DB.GetP[wbIcon, IconProps, iconIcon]];
IF Rope.Equal[icon, ""] THEN {
icon ← DefaultIcon[wbIcon];
DB.SetP[wbIcon, IconProps, iconIcon, DB.S2V[icon]] };
IF Rope.Equal[label, ""] THEN {
type: ATOM;
name: ROPE;
[name, type] ← GetIconProps[wbIcon];
label ← IF type = $Text THEN FileNames.GetShortName[name] ELSE name;
IF NOT readOnly THEN DB.SetP[wbIcon, IconProps, iconLabel, DB.S2V[label]] } };
GetToolArgument: PUBLIC PROC[wbIcon: WBIcon] RETURNS[argument: ROPE] = {
argument ← DB.V2S[DB.GetP[wbIcon, IconProps, iconArgument]]
};
DefaultIcon: PROC[wbIcon: WBIcon] RETURNS[iconName: ROPE] = {
name: ROPE;
type: ATOM;
[name, type] ← GetIconProps[wbIcon];
SELECT type FROM
wbType => iconName ← "Whiteboard";
$ToolRope => iconName ← "Typescript";
$Text => iconName ← "Document";
$Tool => iconName ← name;
ENDCASE => iconName ← "Acorn" };
GetIconProps: PUBLIC PROC[wbIcon: WBIcon] RETURNS[name: ROPE, type: ATOM] = {
name ← DB.V2S[DB.GetP[wbIcon, IconProps, iconName]];
type ← Atom.MakeAtom[DB.V2S[DB.GetP[wbIcon, IconProps, iconType]]] };
SetProps: PUBLIC PROC[wbIcon: WBIcon, name, label, icon: ROPE, type: ATOM] = {
[] ← DB.SetP[wbIcon, IconProps, iconIcon, DB.S2V[icon]];
[] ← DB.SetP[wbIcon, IconProps, iconName, DB.S2V[name]];
[] ← DB.SetP[wbIcon, IconProps, iconLabel, DB.S2V[IF label = NIL THEN name ELSE label]];
[] ← DB.SetP[wbIcon, IconProps, iconType, DB.S2V[Atom.GetPName[type]]]};
END...