DIRECTORY Booting, DB, DBIcons, DBTools, Nut, Process, Rope, ViewerClasses USING [Viewer, ViewerRec], ViewerLocks USING [CallUnderWriteLock, ReleaseWriteLock], ViewerOps, ViewerTools, VirtualDesktops, UserCredentials, UserProfile, WhiteboardDB, WhiteboardDBPrivate, WhiteboardDump, WhiteboardLoad, WhiteboardViewers; WhiteboardDBImpl: CEDAR MONITOR IMPORTS Booting, DB, DBIcons, DBTools, Nut, Process, Rope, ViewerLocks, ViewerOps, ViewerTools, VirtualDesktops, WhiteboardDBPrivate, UserCredentials, UserProfile, WhiteboardViewers, WhiteboardDump, WhiteboardLoad EXPORTS WhiteboardDB SHARES ViewerLocks = BEGIN OPEN DB, WhiteboardDBPrivate; Viewer: TYPE = ViewerClasses.Viewer; ROPE: TYPE = Rope.ROPE; WhiteboardLog: TYPE = LIST OF WhiteboardLogEntry; WhiteboardLogEntry: TYPE = RECORD[action: ATOM, to: Viewer]; whiteboard: PUBLIC ATOM _ WhiteboardDBPrivate.wbType; destroyedList: LIST OF Viewer; ticksToWait: Process.Ticks _ Process.SecondsToTicks[5*60]; stopped: PUBLIC BOOL _ FALSE; -- true if the Rollback proc has been executed and the transaction is to remain closed readOnly: PUBLIC BOOL _ TRUE; -- default to the current database being read only doingRestart: BOOL _ FALSE; -- true during the restart process, to keep iconic viewers from being opened newWhiteboardDB: ROPE; -- the name of a new whiteboard database if pendingChange (it will be used when the first call to CarefullyApply is made) pendingChange: BOOL _ FALSE; -- this is set by the rollback and credentials change procs to remember a potential change of state; it is checked by CarefullyApply, which will call ResetSchema if it is true activity: BOOLEAN _ TRUE; -- true if a database operation has been performed recently WBError: PUBLIC ERROR[reason: ATOM] = CODE; -- the error that may be raised from this interface WatchDBActivity: PROC[] = { WHILE TRUE DO Process.Pause[ticksToWait]; CheckConnection[] ENDLOOP }; CheckConnection: ENTRY PROC[] = { aborted: BOOLEAN _ FALSE; IF stopped THEN RETURN; IF NOT activity THEN CloseTransaction[]; activity _ FALSE }; Close: PUBLIC ENTRY PROC[] = { CloseTransaction[]; DBIcons.Close[]; DBTools.Close[]; stopped _ TRUE }; CloseTransaction: INTERNAL PROC [] = { ENABLE DB.Error, DB.Failure => CONTINUE; trans: DB.Transaction = DB.GetSegmentInfo[$Whiteboard].trans; caughtAborted: BOOL _ FALSE; IF trans # NIL THEN DB.CloseTransaction[trans ! DB.Aborted => { caughtAborted _ TRUE; CONTINUE }]; IF caughtAborted THEN DB.AbortTransaction[trans] }; Display: PUBLIC ENTRY PROC[eName: ROPE, v: Viewer _ NIL] RETURNS [viewer: Viewer] = BEGIN ENABLE UNWIND => NULL; DoDisplay: PROC[] = { viewer _ InternalDisplay[eName, v] }; CarefullyApply[DoDisplay] END; InternalDisplay: PROC[eName: ROPE, v: Viewer _ NIL] RETURNS [Viewer] = BEGIN locked: BOOLEAN _ FALSE; wb: Whiteboard = DeclareWhiteboard[eName]; children: ChildSet = Children[wb]; newViewer: BOOL _ FALSE; nextChild: WBItem; child: Viewer; x, y: INT; DisplayNoteEntity: PROC[] = BEGIN text: ViewerTools.TiogaContents = GetContents[nextChild]; w, h: INT; [w, h] _ Size[nextChild]; child _ WhiteboardViewers.AddTextBox[v, x, y, w, h, text] END; DisplayIconEntity: PROC[] = BEGIN label, icon: ROPE; [label, icon] _ GetDisplayProps[nextChild]; child _ WhiteboardViewers.AddIcon[v, label, DBIcons.GetIcon[icon], x, y]; END; DoDisplay: PROC = { versionNumber: INTEGER; locked _ TRUE; -- now we're inside the forbidden region IF NOT v.newVersion THEN { v.newVersion _ TRUE; WhiteboardViewers.SetMenu[v]; WhiteboardViewers.SetWBName[v, eName]; }; BEGIN gridSize: INT = GetGridSize[wb]; WhiteboardViewers.ResetGrid[v, gridSize] END; versionNumber _ GetVersion[wb]; ViewerOps.AddProp[v, $Version, NEW[INTEGER _ versionNumber]]; WHILE (nextChild _ NextChild[children]) # NIL DO [x, y] _ Position[nextChild]; IF TypeOf[nextChild] = note THEN DisplayNoteEntity[] ELSE DisplayIconEntity[]; StoreEntity[child, DB.NameOf[nextChild]]; ENDLOOP; v.newVersion _ FALSE; ViewerOps.PaintViewer[v, all] }; BEGIN ENABLE UNWIND => { IF locked THEN ViewerLocks.ReleaseWriteLock[v]; IF newViewer THEN ViewerOps.DestroyViewer[v] }; IF v = NIL OR v.class.flavor # WhiteboardViewers.wb THEN { v _ ViewerOps.CreateViewer[flavor: WhiteboardViewers.wb, info: [name: eName, iconic: TRUE]]; newViewer _ TRUE } ELSE v.child _ NIL; -- flushes viewer ViewerLocks.CallUnderWriteLock[DoDisplay, v]; locked _ FALSE; IF NOT doingRestart AND v.iconic AND NOT v.destroyed THEN ViewerOps.OpenIcon[v]; ViewerOps.AddProp[v, $Log, NIL]; -- establish log [] _ Nut.SetFrozenProperty[v, FALSE]; Nut.SetNutInfo[v, $Whiteboard, "Whiteboard", eName]; ViewerOps.AddProp[v, $Lines, NIL]; RETURN[v] END END; Destroy: PUBLIC ENTRY PROC[eName: ROPE] = BEGIN ENABLE UNWIND => NULL; DoDestroy: PROC[] = { wb: Whiteboard = IF Exists[eName] THEN DeclareWhiteboard[eName] ELSE NIL; IF readOnly THEN WBError[$ReadOnly]; IF wb = NIL THEN RETURN ELSE DestroyWhiteboard[wb]; DB.MarkTransaction[DB.GetSegmentInfo[$Whiteboard].trans] }; v: Viewer; CarefullyApply[DoDestroy]; v _ ViewerOps.FindViewer[Rope.Cat["Whiteboard: ", eName]]; IF v # NIL THEN ViewerOps.DestroyViewer[v]; END; New: PUBLIC ENTRY PROC[eName: ROPE] RETURNS[success: BOOL] = BEGIN ENABLE UNWIND => NULL; wbEntity: DB.Entity; DoNew: PROC[] = { IF readOnly THEN WBError[$ReadOnly]; wbEntity _ DeclareWhiteboard[eName]; DB.MarkTransaction[DB.GetSegmentInfo[$Whiteboard].trans] }; CarefullyApply[DoNew]; success _ wbEntity # NIL END; Reset: PUBLIC ENTRY PROC[wb: Viewer] = BEGIN ENABLE UNWIND => NULL; wb.newVersion _ TRUE; ViewerOps.PaintViewer[wb, caption]; CloseTransaction[]; InternalReset[wb]; END; InternalReset: INTERNAL PROC[wb: Viewer] = BEGIN DoRedisplay: PROC[] = { name: ROPE = GetWBName[wb]; IF WhiteboardDBPrivate.Exists[name] THEN [] _ InternalDisplay[name, wb] ELSE ViewerOps.DestroyViewer[wb] }; ViewerOps.AddProp[wb, $Log, NIL]; -- Reset Log [] _ Nut.SetFrozenProperty[wb, FALSE]; ViewerOps.AddProp[wb, $Lines, NIL]; CarefullyApply[DoRedisplay]; END; Save: PUBLIC ENTRY PROC[wb: Viewer] = BEGIN ENABLE UNWIND => NULL; eventLog: WhiteboardLog = NARROW[ViewerOps.FetchProp[wb, $Log], WhiteboardLog]; versionNumber: INTEGER = NARROW[ViewerOps.FetchProp[wb, $Version], REF INTEGER]^; DoSave: PROC[] = { IF readOnly THEN WBError[$ReadOnly]; BEGIN wbEntity: Whiteboard = DeclareWhiteboard[GetWBName[wb]]; IF GetVersion[wbEntity] # versionNumber THEN WBError[$WrongVersion]; FOR log: WhiteboardLog _ eventLog, log.rest UNTIL log = NIL DO SELECT log.first.action FROM $NewIcon => DoNewIcon[wbEntity, log.first.to]; $NewBox => DoNewBox[wbEntity, log.first.to]; $Delete => DoDelete[log.first.to]; $EditText => DoEditText[log.first.to]; $Grow => DoGrow[log.first.to]; $Move => DoMove[log.first.to]; $Erase => DoErase[wbEntity]; ENDCASE; ENDLOOP; SetGridSize[wbEntity, WhiteboardViewers.GetGrid[wb]]; SetVersion[wbEntity, versionNumber + 1]; DB.MarkTransaction[DB.GetSegmentInfo[$Whiteboard].trans] END }; [] _ wb.class.scroll[wb, thumb, 0]; CarefullyApply[DoSave]; DestroyViewerList[]; InternalReset[wb] END; DestroyViewerList: PROC[] = { FOR l: LIST OF Viewer _ destroyedList, l.rest UNTIL l = NIL DO l.first.props _ NIL; l.first.data _ NIL ENDLOOP; destroyedList _ NIL }; GetWBName: PUBLIC PROC[wb: Viewer] RETURNS [name: ROPE] = BEGIN domain: ROPE; [domain ~ domain, entity ~ name] _ Nut.GetNutInfo[wb]; IF Rope.Equal[domain, "Whiteboard"] THEN RETURN; RETURN[NIL] END; WBExists: PUBLIC ENTRY PROC[eName: ROPE] RETURNS [alreadyExists: BOOL] = BEGIN ENABLE UNWIND => NULL; DoCheck: PROC[] = {alreadyExists _ Exists[eName]}; CarefullyApply[DoCheck]; END; CopyWB: PUBLIC ENTRY PROC[from, to: ROPE] = BEGIN ENABLE UNWIND => NULL; DoCopy: INTERNAL PROC[] = { IF readOnly THEN WBError[$ReadOnly]; BEGIN fromWB: Whiteboard = DeclareWhiteboard[from]; toWB: Whiteboard = DeclareWhiteboard[to]; children: ChildSet = Children[fromWB]; FOR child: WBItem _ NextChild[children], NextChild[children] UNTIL child = NIL DO IF TypeOf[child] = note THEN CopyNote[toWB, child] ELSE CopyIcon[toWB, child] ENDLOOP; DB.MarkTransaction[DB.GetSegmentInfo[$Whiteboard].trans] END }; CarefullyApply[DoCopy] END; CopyNote: INTERNAL PROC[to: Whiteboard, note: WBNote] = BEGIN text: ViewerTools.TiogaContents = GetContents[note]; x, y: INT; w, h: INT; [x, y] _ Position[note]; [w, h] _ Size[note]; [] _ NewNote[to, x, y, w, h, text] END; CopyIcon: INTERNAL PROC[to: Whiteboard, icon: WBIcon] = BEGIN x, y: INT; name, label, iconName, argument: ROPE; type: ATOM; [x, y] _ Position[icon]; [label, iconName] _ GetDisplayProps[icon]; [name, type] _ WhiteboardDBPrivate.GetIconProps[icon]; argument _ WhiteboardDBPrivate.GetToolArgument[icon]; [] _ WhiteboardDBPrivate.NewIcon[to, x, y, name, label, iconName, argument, type] END; EnumerateWhiteboards: PUBLIC ENTRY PROC[eName: ROPE _ NIL] RETURNS [wbList: LIST OF ROPE] = BEGIN ENABLE UNWIND => NULL; EnumWBs: PROC[] = { IF eName = NIL THEN { entityList: LIST OF Whiteboard = Enumerate[]; FOR eL: LIST OF Whiteboard _ entityList, eL.rest UNTIL eL = NIL DO wbList _ CONS[DB.NameOf[eL.first], wbList]; ENDLOOP } ELSE { entityList: LIST OF WBItem; children: ChildSet = Children[DeclareWhiteboard[eName], TRUE]; FOR child: WBItem _ NextChild[children], NextChild[children] UNTIL child = NIL DO entityList _ CONS[child, entityList]; ENDLOOP; FOR eL: LIST OF Whiteboard _ entityList, eL.rest UNTIL eL = NIL DO wbList _ CONS[WhiteboardDBPrivate.GetIconProps[eL.first].name, wbList]; ENDLOOP } }; CarefullyApply[EnumWBs] END; Dump: PUBLIC ENTRY PROC[to: ROPE] = { ENABLE UNWIND => NULL; DoDump: INTERNAL PROC[] = {WhiteboardDump.DumpToFile[to] }; CarefullyApply[DoDump] }; Load: PUBLIC ENTRY PROC[from: ROPE] = { ENABLE UNWIND => NULL; DoLoad: INTERNAL PROC[] = { IF readOnly THEN WBError[$ReadOnly]; WhiteboardLoad.LoadFromFile[from] }; CarefullyApply[DoLoad] }; NewIcon: PUBLIC ENTRY PROC[wb: Viewer, x, y: INT, name: ROPE, type: ATOM, icon: ROPE, label: ROPE _ NIL, argument: ROPE _ NIL] RETURNS [new: Viewer] = BEGIN ENABLE UNWIND => NULL; new _ WhiteboardViewers.AddIcon[wb, IF label = NIL THEN name ELSE label, DBIcons.GetIcon[icon], x, y]; ViewerOps.AddProp[new, $EntityName, name]; ViewerOps.AddProp[new, $IconLabel, label]; ViewerOps.AddProp[new, $IconType, type]; ViewerOps.AddProp[new, $IconIcon, icon]; ViewerOps.AddProp[new, $IconArgument, argument]; LogAction[wb, $NewIcon, new]; ViewerOps.PaintViewer[new, all]; ViewerOps.PaintViewer[wb, caption]; END; NewBox: PUBLIC ENTRY PROC[wb: Viewer, x, y, w, h: INT, contents: ViewerTools.TiogaContents _ NIL] RETURNS [new: Viewer] = BEGIN ENABLE UNWIND => NULL; new _ WhiteboardViewers.AddTextBox[wb, x, y, w, h, contents]; LogAction[wb, $NewBox, new]; ViewerOps.PaintViewer[new, all]; ViewerOps.PaintViewer[wb, caption]; END; Delete: PUBLIC ENTRY PROC[child: Viewer] = BEGIN ENABLE UNWIND => NULL; logIt: BOOL = NOT WhiteboardViewers.DontLog[child]; props: Atom.PropList = child.props; data: REF ANY = child.data; IF logIt THEN LogAction[child.parent, $Delete, child]; ViewerOps.DestroyViewer[viewer: child, paint: FALSE]; IF logIt THEN {child.props _ props; child.data _ data} END; EditText: PUBLIC ENTRY PROC[box: Viewer] = BEGIN ENABLE UNWIND => NULL; LogAction[box.parent, $EditText, box]; END; Grow: PUBLIC ENTRY PROC[box: Viewer] = BEGIN ENABLE UNWIND => NULL; LogAction[box.parent, $Grow, box]; END; Move: PUBLIC ENTRY PROC[child: Viewer] = BEGIN ENABLE UNWIND => NULL; IF NOT WhiteboardViewers.DontLog[child] THEN LogAction[child.parent, $Move, child]; END; Erase: PUBLIC ENTRY PROC[wb: Viewer] = BEGIN ENABLE UNWIND => NULL; LogAction[wb, $Erase, wb]; wb.child _ NIL; -- flushes viewer ViewerOps.PaintViewer[wb, client]; END; LogAction: PROC[wb: Viewer, action: ATOM, v: Viewer] = BEGIN log: WhiteboardLog _ NARROW[ViewerOps.FetchProp[wb, $Log], WhiteboardLog]; AddToEnd: PROC[log: WhiteboardLog, entry: WhiteboardLogEntry] RETURNS[WhiteboardLog] = { IF log = NIL THEN RETURN[LIST[entry]] ELSE RETURN[CONS[log.first, AddToEnd[log.rest, entry]]] }; log _ AddToEnd[log, [action, v]]; ViewerOps.AddProp[wb, $Log, log]; END; DoNewIcon: PROC[wb: Whiteboard, child: Viewer] = BEGIN name: ROPE = NARROW[ViewerOps.FetchProp[child, $EntityName], ROPE]; label: ROPE = NARROW[ViewerOps.FetchProp[child, $IconLabel], ROPE]; type: ATOM = NARROW[ViewerOps.FetchProp[child, $IconType], ATOM]; icon: ROPE = NARROW[ViewerOps.FetchProp[child, $IconIcon], ROPE]; argument: ROPE = NARROW[ViewerOps.FetchProp[child, $IconArgument], ROPE]; newIcon: WBItem = WhiteboardDBPrivate.NewIcon[wb, child.wx, child.wy, name, label, icon, argument, type]; StoreEntity[child, DB.NameOf[newIcon]] -- in case subsequent operations in the log reference this viewer (eg., to move it) END; DoNewBox: PROC[wb: Whiteboard, box: Viewer] = BEGIN text: ViewerTools.TiogaContents = ViewerTools.GetTiogaContents[box]; newNote: WBItem = WhiteboardDBPrivate.NewNote[wb, box.wx, box.wy, box.ww, box.wh, text]; StoreEntity[box, DB.NameOf[newNote]] -- in case subsequent operations in the log reference this viewer (eg., to move it) END; DoDelete: PROC[child: Viewer] = BEGIN entity: WBItem = FetchEntity[child]; WhiteboardDBPrivate.Destroy[entity]; destroyedList _ CONS[child, destroyedList]; END; DoEditText: PROC[box: Viewer] = BEGIN note: WBNote = FetchEntity[box]; text: ViewerTools.TiogaContents = ViewerTools.GetTiogaContents[box]; SetContents[note, text] END; DoGrow: PROC[box: Viewer] = BEGIN note: WBNote = FetchEntity[box]; WhiteboardDBPrivate.Grow[note, box.ww, box.wh] END; DoMove: PROC[child: Viewer] = BEGIN entity: WBItem = FetchEntity[child]; WhiteboardDBPrivate.Move[entity, child.wx, child.wy] END; DoErase: PROC[wb: Whiteboard] = BEGIN name: ROPE = NameOf[wb]; WhiteboardDBPrivate.DestroyWhiteboard[wb]; [] _ DeclareWhiteboard[name] END; FetchEntity: PUBLIC PROC[v: Viewer] RETURNS[e: DB.Entity] = { name: ROPE = NARROW[ViewerOps.FetchProp[v, $Entity], ROPE]; IF name = NIL THEN RETURN[NIL]; SELECT v.class.flavor FROM WhiteboardViewers.text => e _ FetchWBItem[name, note]; WhiteboardViewers.icon => e _ FetchWBItem[name, icon]; ENDCASE => e _ NIL }; StoreEntity: PUBLIC PROC[v: Viewer, name: ROPE] = { ViewerOps.AddProp[v, $Entity, name ] }; GetIconProps: PUBLIC ENTRY PROC[wbIcon: Viewer] RETURNS[ name: ROPE, type: ATOM ] = { ENABLE UNWIND => NULL; DoGet: PROC[] = { iconEntity: WBIcon = FetchEntity[wbIcon]; [name, type] _ WhiteboardDBPrivate.GetIconProps[iconEntity] }; name _ NARROW[ViewerOps.FetchProp[wbIcon, $EntityName]]; IF name # NIL THEN type _ NARROW[ViewerOps.FetchProp[wbIcon, $IconType]] ELSE CarefullyApply[DoGet] }; GetToolArgument: PUBLIC ENTRY PROC[wbIcon: Viewer] RETURNS[ argument: ROPE] = { ENABLE UNWIND => NULL; DoGet: PROC[] = { iconEntity: WBIcon = FetchEntity[wbIcon]; IF iconEntity = NIL THEN {argument _ NIL; RETURN}; argument _ WhiteboardDBPrivate.GetToolArgument[iconEntity] }; argument _ NARROW[ViewerOps.FetchProp[wbIcon, $IconArgument]]; IF argument = NIL THEN CarefullyApply[DoGet]; ViewerOps.AddProp[wbIcon, $IconArgument, argument] }; EstablishWhiteboardDB: PUBLIC ENTRY PROC[DBFile: ROPE _ NIL] = { ENABLE UNWIND => NULL; IF DBFile = NIL THEN IF WhiteboardDBPrivate.WBSegment # NIL THEN newWhiteboardDB _ WhiteboardDBPrivate.WBSegment ELSE newWhiteboardDB _ UserProfile.Token[key: "Whiteboard.Segment", default: "[Luther.Alpine]Whiteboard.segment"] ELSE newWhiteboardDB _ DBFile; stopped _ FALSE; pendingChange _ TRUE; InternalResetViewers[newWhiteboardDB # WhiteboardDBPrivate.WBSegment] }; InternalResetViewers: INTERNAL PROC[destroy: BOOL] = { IF destroy THEN { DestroyProc: ViewerOps.EnumProc = { IF v.class.flavor = $Whiteboard THEN ViewerOps.DestroyViewer[v]; RETURN[TRUE] }; aborted: BOOL _ FALSE; CloseTransaction[]; VirtualDesktops.EnumerateViewers[enum: DestroyProc] } ELSE { ResetProc: INTERNAL ViewerOps.EnumProc = { IF v.class.flavor = $Whiteboard AND NOT v.newVersion AND NOT v.destroyed THEN InternalReset[v]; RETURN[TRUE] }; doingRestart _ TRUE; VirtualDesktops.EnumerateViewers[enum: ResetProc]; doingRestart _ FALSE } }; CarefullyApply: INTERNAL PROC [proc: PROC[]] ~ { ENABLE BEGIN DB.Error => ERROR WBError[$InternalError]; DB.Failure => ERROR WBError[$ServerDown]; DB.Aborted => ERROR WBError[$TransactionAbort]; END; aborted: BOOL _ FALSE; IF stopped THEN RETURN; -- don't bother trying to do the operation activity _ TRUE; IF pendingChange THEN { pendingChange _ FALSE; Initialize[newWhiteboardDB]; readOnly _ DB.GetSegmentInfo[whiteboard].readOnly }; BEGIN ENABLE DB.Aborted => { aborted _ TRUE; CONTINUE }; [] _ WhiteboardDBPrivate.ResetSchema[]; proc[] END; IF NOT aborted THEN RETURN; -- no aborted occurred DB.AbortTransaction[DB.GetSegmentInfo[$Whiteboard].trans]; WhiteboardDBPrivate.ResetSchema[]; proc[]; -- don't bother trying to restart here -- }; CloseWB: ENTRY Booting.CheckpointProc = { ENABLE UNWIND => NULL; CloseTransaction[]; stopped _ TRUE }; OpenUp: Booting.RollbackProc = { DB.Initialize[nCachePages: 256] }; NewUserReset: ENTRY UserCredentials.CredentialsChangeProc = { ENABLE UNWIND => NULL; DestroyProc: ViewerOps.EnumProc = { IF v.class.flavor = $Whiteboard THEN ViewerOps.DestroyViewer[v]; RETURN[TRUE] }; CloseTransaction[]; VirtualDesktops.EnumerateViewers[enum: DestroyProc] }; ProfileChangeReset: ENTRY UserProfile.ProfileChangedProc = { ENABLE UNWIND => NULL; DestroyProc: ViewerOps.EnumProc = { IF v.class.flavor = $Whiteboard THEN ViewerOps.DestroyViewer[v]; RETURN[TRUE] }; SELECT reason FROM firstTime, rollBack => { newDB: ROPE = UserProfile.Token[key: "Whiteboard.Segment", default: "[Luther.Alpine]Whiteboard.segment"]; IF NOT Rope.Equal[WhiteboardDBPrivate.WBSegment, newDB] THEN { newWhiteboardDB _ newDB; pendingChange _ TRUE } }; -- don't open up the new database yet; simply remember that it must be done rollBack => RETURN; edit => { -- Only do something if the Whiteboard segment has changed newWhiteboardDB _ UserProfile.Token[key: "Whiteboard.Segment", default: "[Luther.Alpine]Whiteboard.segment"]; IF NOT Rope.Equal[newWhiteboardDB, WhiteboardDBPrivate.WBSegment] THEN { pendingChange _ TRUE; InternalResetViewers[TRUE] } } ENDCASE }; TRUSTED { [] _ EstablishWhiteboardDB[]; Process.Detach[FORK WatchDBActivity[]]; Booting.RegisterProcs[c: CloseWB, r: OpenUp]; UserCredentials.RegisterForChange[NewUserReset]; UserProfile.CallWhenProfileChanges[ProfileChangeReset] } END... ²WhiteboardDBImpl.mesa Copyright (C) 1984 by Xerox Corporation. All rights reserved. Last edited by Jennifer Widom, August 24, 1984 8:38:52 pm PDT Donahue, January 21, 1985 8:51:04 am PST (Corrected destruction of viewer properties when playing log; simplified CarefullyApply) (Changed reset to close transaction before doing redisplay so that cached pages aren't used -- this gets the proper stuff redisplayed!) (Added WBError and changed error handling behavior in the code -- now most of it is in CarefullyApply, where it should have been all along) Last Edited by: Winkler, December 18, 1984 4:49:16 pm PST Currently action can be one of: $NewIcon $NewBox $Delete $EditText $Grow $Move $Erase Monitoring database activity Don't keep a transaction open too long. Routines that read and/or write the database If v.newVersion then this is a reset and the name and menu are properly set Must make sure that the grid size is consistent with the current DB contents obtain version information Check if there's a viewer that needs to be destroyed Now it's safe to throw away any destroyed viewers (ones on the destroyedList); they either participated in a committed transaction (and thus can have their properties released) or they no longer exist on the whiteboard in any case Commit the transaction Routines that log whiteboard events w/o accessing the database Make sure the properties and clientdata don't go away just yet Playing the log Commit a $NewIcon action Commit a $NewBox action Commit a $Delete action Save the child on the destroyedList so that you can release the props and data if the transaction succeeds Commit an $EditText action Commit a $Grow action Commit a $Move action Commit an $Erase action Support routines Tool manipulation If the entity name is NIL, then don't bother looking in the database for an argument Database initialization Sets newWhiteboardDB to value of either DBFile or user profile entry (if a whiteboard DB was previously opened and the argument was NIL, then remember the previous DB file); also throws away old viewers on previous DB; The value of success will be FALSE only if InternalResetViewers fails to reestablish a connection Close transaction if there's one open Destroy all Whiteboard viewers Leave edited or dead whiteboards alone!! The enumeration will fail and stop if the reset fails; this also sets the returned value of success Close transaction if there's one open Destroy all whiteboard viewers Initialization �Ê]˜Jšœ™Jšœ=™=šœ™Jšœ.™.J™(J™XJ™‡J™‹J™9J˜—šÏk ˜ J˜Jš�œ˜J˜J˜J˜J˜J˜Jšœ�œ˜(Jšœ �œ(˜9J˜ J˜ J˜J˜J˜ J˜ Jšœ˜J˜J˜Jšœ˜J˜—šœ�œ�˜š�œ˜Jšœ �œÂ˜Í—Jš�œ ˜Jš�œ˜J˜Jš�œ�œ�œ˜#J˜—Jšœ�œ˜$J˜Jš�œ�œ�œ˜J˜Jšœ�œ�œ�œ˜1J˜šœ�œ�œ �œ˜<šœ™Jšœ™J™Jšœ™Jšœ ™ Jšœ™Jšœ™J™——J˜Jšœ �œ�œ˜5J˜Jšœ�œ�œ˜Ihead1™˜Jšœ:˜:J˜Jšœ � œ�œÏcV˜tJ˜Jšœ �œ�œ�œž2˜PJ˜Jšœ�œ�œžL˜hJ˜Jšœ�œžz˜’J˜Jšœ�œ�œž°˜ÎJ˜Jšœ �œ�œž;˜WJ˜Jš œ �œ�œ �œ�œž3˜`J˜šÏnœ�œ˜J™'š�œ�œ�˜ Jšœ˜Jšœ˜Jš�œ˜ —J˜—šŸœ�œ�œ˜!Jšœ �œ�œ˜Jš�œ �œ�œ˜Jš�œ�œ �œ˜(Jšœ �œ˜—J˜šŸœ�œ�œ�œ˜Jšœ˜J˜J˜Jšœ �œ˜—J˜šŸœ�œ�œ˜&Jš�œ�œ�œ �œ˜(Jšœ�œ�$œ˜=Jšœ�œ�œ˜š�œ �œ�˜Jš�œ�œ�œ�œ˜N—Jš�œ�œ�œ˜4——K™,˜šŸœ�œ�œ�œ�œ�œ�œ˜Sš�˜Jš�œ�œ�œ˜JšŸ œ�œ,˜;Jšœ˜Jš�œ˜——J˜š Ÿœ�œ�œ�œ�œ ˜Gš�˜Jšœ�œ�œ˜Jšœ*˜*Jšœ"˜"Jšœ �œ�œ˜J˜J˜Jšœ�œ˜ šŸœ�œ˜š�˜Jšœ9˜9Jšœ�œ˜ Jšœ˜Jšœ9˜9Jš�œ˜——šŸœ�œ˜š�˜Jšœ �œ˜J˜+JšœI˜IJš�œ˜——šŸ œ�œ˜Jšœ�œ˜Jšœ �œž(˜8JšœK™Kš�œ�œ�œ˜Jšœ�œ˜J˜J˜&J˜—JšœL™Lš�˜Jšœ �œ˜ Jšœ(˜(Jš�œ˜—J™Jšœ˜Jšœ�œ�œ˜=š�œ%�œ�˜0Jšœ˜Jš�œ�œ�œ˜NJšœ�œ˜)Jš�œ˜—Jšœ�œ˜J˜ —J˜š�˜š�œ�œ˜Jš�œ�œ!˜/Jš�œ �œ˜/—š�œ�œ�œ'�œ˜:JšœU�œ˜\Jšœ �œ˜Jš�œ �œž˜%—J˜-Jšœ �œ˜Jš �œ�œ�œ �œ�œ �œ˜PJšœ�œž˜1Jšœ�œ˜%J˜4Jšœ�œ˜"Jš�œ˜ Jš�˜—Jš�œ˜——J˜š Ÿœ�œ�œ�œ�œ˜)š�˜Jš�œ�œ�œ˜šŸ œ�œ˜Jš œ�œ�œ�œ�œ˜IJš�œ �œ˜$Jš �œ�œ�œ�œ�œ˜3Jš�œ�$œ˜8Jšœ˜—J˜ Jšœ˜J™4J˜:Jš�œ�œ�œ˜+Jš�œ˜——J˜J˜šŸœ�œ�œ�œ�œ�œ �œ˜<š�˜Jš�œ�œ�œ˜Jšœ �œ˜šŸœ�œ˜Jš�œ �œ˜$Jšœ$˜$Jš�œ�$œ˜;—Jšœ˜Jšœ�˜Jš�œ˜——J˜šŸœ�œ�œ�œ˜&š�˜Jš�œ�œ�œ˜Jšœ�œ˜J˜#J˜Jšœ˜Jš�œ˜——J˜šŸ œ�œ�œ˜*š�˜šŸ œ�œ˜Jšœ�œ˜Jš�œ"�œ˜GJš�œ˜#—Jšœ�œž ˜.Jšœ�œ˜&Jšœ�œ˜#Jšœ˜Jš�œ˜J˜——šŸœ�œ�œ�œ˜%š�˜Jš�œ�œ�œ˜Jšœ�œ/˜OJš œ�œ�œ$�œ�œ˜QšŸœ�œ˜Jš�œ �œ˜$š�˜Jšœ8˜8Jš�œ&�œ˜Dš�œ)�œ�œ�˜>š�œ�˜Jšœ.˜.Jšœ,˜,Jšœ"˜"Jšœ&˜&Jšœ˜Jšœ˜Jšœ˜Jš�œ˜—Jš�œ˜—Jšœ5˜5Jšœ(˜(Jš�œ�$œ˜8Jš�œ˜——Jšœ#˜#J˜Jšœæ™æJšœ˜Jšœ˜Jš�œ˜——J˜šŸœ�œ˜š �œ�œ�œ �œ�œ�˜>Jšœ�œ˜Jšœ�˜Jš�œ˜—Jšœ�œ˜—J˜š Ÿ œ�œ�œ �œ�œ˜9š�˜Jšœ�œ˜ Jšœ6˜6Jš�œ"�œ�œ˜0Jš�œ�œ˜ Jš�œ˜——J˜šŸœ�œ�œ�œ�œ�œ�œ˜Hš�˜Jš�œ�œ�œ˜JšŸœ�œ&˜3Jšœ˜Jš�œ˜——J˜š Ÿœ�œ�œ�œ �œ˜+š�˜Jš�œ�œ�œ˜šŸœ�œ�œ˜Jš�œ �œ˜$š�˜Jšœ-˜-Jšœ)˜)Jšœ&˜&š�œ:�œ �œ�˜Qš�œ�œ˜2Jš�œ˜—Jš�œ˜—J™Jš�œ�$œ˜8Jš�œ˜——Jšœ˜Jš�œ˜——J˜J˜šŸœ� œ ˜7š�˜J˜4Jšœ�œ˜ Jšœ�œ˜ J˜J˜Jšœ"˜"Jš�œ˜——J˜šŸœ�œ�œ ˜7š�˜Jšœ�œ˜ Jšœ!�œ˜&Jšœ�œ˜ Jšœ˜Jšœ*˜*Jšœ6˜6J˜5JšœQ˜QJš�œ˜——J˜šŸœ�œ�œ�œ�œ�œ�œ �œ�œ�œ˜[š�˜Jš�œ�œ�œ˜šŸœ�œ˜š�œ �œ�œ˜Jšœ �œ�œ˜-š �œ�œ�œ"�œ�œ�˜BJšœ �œ�œ˜+Jš�˜—Jšœ˜—š�œ˜Jšœ �œ�œ˜Jšœ8�œ˜>š�œ:�œ �œ�˜QJšœ �œ˜%Jš�œ˜—š �œ�œ�œ"�œ�œ�˜BJšœ �œ:˜GJš�œ˜ ——Jšœ˜—Jšœ˜Jš�œ˜——J˜š Ÿœ�œ�œ�œ�œ˜%Jš�œ�œ�œ˜JšŸœ�œ�œ&˜;Jšœ˜—J˜š Ÿœ�œ�œ�œ�œ˜'Jš�œ�œ�œ˜šŸœ�œ�œ˜Jš�œ �œ˜$Jšœ$˜$—Jšœ˜——K™>˜šŸœ�œ�œ�œ�œ�œ�œ�œ �œ�œ �œ�œ�œ˜–š�˜Jš�œ�œ�œ˜Jš œ$�œ �œ�œ�œ%˜fJšœ*˜*J˜*Jšœ(˜(J˜(Jšœ0˜0J˜J˜ J˜#Jš�œ˜——J˜šŸœ�œ�œ�œ�œ(�œ�œ˜yš�˜Jš�œ�œ�œ˜Jšœ=˜=J˜J˜ J˜#Jš�œ˜—J˜—šŸœ�œ�œ�œ˜*š�˜Jš�œ�œ�œ˜Jšœ�œ�œ"˜3Jšœ$˜$Jšœ�œ�œ˜Jšœ>™>Jš�œ�œ)˜6Jšœ.�œ˜5Jš�œ�œ)˜6š�œ˜J˜———šŸœ�œ�œ�œ˜*š�˜Jš�œ�œ�œ˜Jšœ&˜&Jš�œ˜—J˜—šŸœ�œ�œ�œ˜&š�˜Jš�œ�œ�œ˜Jšœ"˜"Jš�œ˜J˜——šŸœ�œ� œ˜(š�˜Jš�œ�œ�œ˜š�œ�œ"�œ˜-J˜&—Jš�œ˜—J˜—šŸœ�œ�œ�œ˜&š�˜Jš�œ�œ�œ˜Jšœ˜Jšœ �œž˜!J˜"Jš�œ˜——J˜šŸ œ�œ�œ˜6š�˜Jšœ�œ/˜JšŸœ�œ/˜=š�œ˜š �œ�œ�œ�œ�œ˜%Jš�œ�œ�œ*˜:———Jšœ!˜!Jšœ!˜!Jš�œ˜———K™˜šŸ œ�œ!˜0Jšœ™š�˜Jšœ�œ�œ*�œ˜CJšœ�œ�œ)�œ˜CJšœ�œ�œ(�œ˜AJšœ�œ�œ(�œ˜AJšœ �œ�œ,�œ˜IJšœi˜iJšœ�œžS˜zJš�œ˜——J˜JšŸœ�œ˜-šœ™š�˜J˜DJšœX˜XJšœ�œžS˜xJš�œ˜——J˜šŸœ�œ˜Jšœ™š�˜Jšœ$˜$Jšœ$˜$Jšœj™jJšœ�œ˜+Jš�œ˜——J˜JšŸ œ�œ˜šœ™š�˜Jšœ ˜ J˜DJ˜Jš�œ˜——J˜JšŸœ�œ˜šœ™š�˜Jšœ ˜ J˜.Jš�œ˜J˜——JšŸœ�œ˜šœ™š�˜Jšœ$˜$J˜4Jš�œ˜——J˜JšŸœ�œ˜šœ™š�˜Jšœ�œ˜Jšœ*˜*J˜Jš�œ˜———K™˜š Ÿ œ�œ�œ �œ�œ ˜=Jšœ�œ�œ"�œ˜;Jš �œ�œ�œ�œ�œ˜š�œ�˜Jšœ6˜6Jšœ6˜6—š�œ �œ˜J˜——šŸ œ�œ�œ�œ˜3Jšœ'˜'——K™˜šŸ œ�œ�œ�œ�œ�œ�œ˜UJš�œ�œ�œ˜šŸœ�œ˜Jšœ)˜)Jšœ>˜>—Jšœ�œ+˜8š�œ�œ�˜Jšœ�œ(˜5—Jš�œ˜—J˜š Ÿœ�œ�œ�œ�œ �œ˜OJš�œ�œ�œ˜šŸœ�œ˜Jšœ)˜)JšœT™TJš �œ�œ�œ �œ�œ˜2Jšœ=˜=—Jšœ �œ-˜>Jš�œ �œ�œ˜-Jšœ5˜5——™š Ÿœ�œ�œ�œ �œ�œ˜@JšœÚ™ÚJ™aJš�œ�œ�œ˜š�œ �œ�˜Jš�œ!�œ�œ0˜[Jš�œw˜{—Jš�œ˜Jšœ �œ˜Jšœ�œ˜JšœH˜H—J˜J˜šŸœ�œ�œ �œ˜6š�œ �œ˜šœ#˜#Jš�œ�œ˜@Jš�œ�œ˜—Jšœ �œ�œ˜J™%Jšœ˜J™Jšœ5˜5—š�œ˜šœ �œ˜*Jšœ(™(š �œ�œ�œ�œ�œ ˜HJš�œ˜—Jš�œ�œ˜—Jšœ�œ˜Jšœ2˜2Jšœc™cJšœ�œ˜—J˜—šŸœ�œ�œ�œ˜0š�œ�˜ Jš�œ �œ˜*Jš�œ �œ˜)Jš�œ �œ˜/Jš�œ˜—Jšœ �œ�œ˜Jš�œ �œ�œž*˜CJšœ �œ˜š�œ�œ˜Jšœ�œ˜Jšœ˜Jšœ4˜4—š�˜Jš�œ�œ�œ�œ˜2Jšœ'˜'Jšœ˜Jš�œ˜—Jš �œ�œ �œ�œž˜2Jš�œ�$œ˜:Jšœ"˜"JšœÏbž)˜1Jšœ˜—J˜šœ �œ˜)Jš�œ�œ�œ˜Jšœ˜šœ �œ˜J˜——šœ �œ ˜CJ˜—šœ�œ*˜=Jš�œ�œ�œ˜šœ#˜#Jš�œ�œ˜@Jš�œ�œ˜—J™%Jšœ˜J™Jšœ6˜6—J˜J˜šœ�œ#˜Jšœ˜Jšœ�œžJœ˜f——Jšœ �œ˜šœ ž:˜DJšœx˜xš�œ�œ<�œ˜HJšœ�œ˜Jšœ�œ˜——Jš�œ˜ ——J˜—K™˜š�œ˜ J˜Jšœ�œ˜'Jšœ-˜-Jšœ0˜0Jšœ8˜8——J˜Jš�œ˜J˜—…—G¤j³