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: BOOL ← FALSE;
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:
BOOL ←
TRUE] = {
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:
BOOL ←
FALSE]
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]]]};