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³