(**** * * This file defines objects and operations related to the Desk Drawer, * including Overlays and Sticky Notes * *) module DeskDrawer; from DrawingTools import Shape; from Lecture import Presentation, Topic, Content, Text, Enumerator; export DeskDrawer, Sticky, Snapshot, Overlay, StickyNote; obj DeskDrawer is components: snapshots:Snapshot* and notes:StickyNote*; description: (* Models the desk drawer shown on the side of the presentation window. *); end DeskDrawer; obj Sticky is components: owner:string and statusText:string; description: (* A sticky is the base class for overlays and sticky notes, the two objects that can be linked to a topic. The owner is the userID of the creator of the sticky. Status is the status text of the sticky - either 'Unlinked' or a preview of the topic linked to. The Topic is the Topic that the Sticky is linked to (if any). *); end Sticky; obj Snapshot inherits from Sticky components: Background and overlays:Overlay*; description: (* A Snapshot has a "snapshot" of the lecture at a given point (the Background). A Snapshot may have one or more Overlays. The order they are positioned in the list determines their z-order on the Snapshot (first item in the list is top-most seen on the Snapshot). *); end Snapshot; obj Background; obj Overlay components: permissions:SecurityLevel and backgroundOpacity:integer and shapeOpacity:integer and shapes:Shape*; description: (* An overlay is a surface for drawing shapes over an opaque background or the lecture shown in the containing Snapshot. Permissions detail who can edit and view this Overlay. BackgroundOpacity and ShapeOpacity is a level of transparency from 0 - 100 (opaque - transparent). BackgroundOpacity controls the opacity for the background of the Overlay. ShapeOpacity controls the opacity for all of the shapes contained in the Overlay. An Overlay may have one or more Shape objects (as drawn by the user). *); end Overlay; obj SecurityLevel is components: editors:string* and viewers:string*; description: (* A security level consists of a list of editors and viewers. Both are lists of user ids that represent distinct users in the class roster. Any student whose user id in the editors list can edit the given Snapshot. Any student whose user id in the the viewers list can view the Snapshot. *); end SecurityLevel; operation CreateSnapshot is inputs: p:Presentation, dd:DeskDrawer, s:Snapshot; outputs: dd':DeskDrawer; description: (* Creates an overlay with a stored state of how the Presentation is at the moment. Adds the overlay to the DeskDrawer. *); precondition: (* * None. *) ; postcondition: (* * The new snapshot is in the output DeskDrawer. *) forall (snap:Snapshot) (snap in dd'.snapshots) iff (snap in dd.snapshots or snap = s); end CreateSnapshot; operation DeleteSnapshot is inputs: dd:DeskDrawer, s:Snapshot; outputs: dd':DeskDrawer; description: (* Removes the Snapshot from the DeskDrawer's Snapshot collection. *); precondition: (* * The given Snapshot is in the given DeskDrawer *) s in dd.snapshots; postcondition: (* * A Snapshot is in the output DeskDrawer if and only if the Snapshot * is not the given one and the Snapshot is in the input DeskDrawer. *) forall (s':Snapshot) (s' in dd'.snapshots) iff ((s' != s) and (s' in dd.snapshots)); end DeleteSnapshot; operation LinkSnapshot is inputs: t:Topic, s:Snapshot; outputs: t':Topic, s':Snapshot; description: (* Links the Snapshot to the specified Topic. *); precondition: (* * None *) ; postcondition: (* * A Snapshot is in the output Topic's stickies list if and only * if it is in the input Topic's stickies list or it is the input * Snapshot *) forall (snap:Snapshot) (snap in t'.stickies) iff (snap in t.stickies or snap = s) and (* * The ouput Snapshot's statusText is set to the first string of content * within the Topic, if one exists *) forall (contentText:Text | contentText in t'.content.contentList) if (s'.statusText = "Unlinked") then s'.statusText = contentText and (* * If no contentText was set, set it to the Topic's enum. *) s'.statusText = t.enum; end LinkSnapshot; operation UnlinkSnapshot is inputs: t:Topic, s:Snapshot; outputs: t':Topic, s':Snapshot; description: (* Removes the linking between the Snapshot and its linked Topic. *); precondition: (* * The input Snapshot is in the input Topic's list of Stickys. *) s in t.stickies; postcondition: (* * A Snapshot is in the output Topic's list if and only if it is in the * the input Topic's list and it is not the input Snapshot. *) forall (snap:Snapshot) (snap in t'.stickies) iff (snap in t.stickies and snap != s) and (* * The output Snapshot's statusText is set to "Unlinked" *) s'.statusText = "Unlinked"; end UnlinkSnapshot; operation AddOverlay is inputs: s:Snapshot, o:Overlay; outputs: s':Snapshot; description: (* Adds the input Overlay to the input Snapshot's list of Overlays. The input Overlay is added to the bottom of the z-order (as the last item in the Snapshot's list.) *); precondition: (* * None *) ; postcondition: (* * The output Snapshot's Overlay list has one more item than the input * Snapshot's list. *) #(s'.overlays) = #(s.overlays) + 1 and (* * All Overlays in the input Snapshot's list are in the output Snapshot's * list in the same order. *) (forall (i:integer | (i >= 1) and (i < #(s.overlays))) s'.overlays[i] = s.overlays[i]) and (* * The input Overlay is the last item in the output Snapshot's list. *) s'.overlays[#(s'.overlays)] = o; end AddOverlay; operation DeleteOverlay is inputs: s:Snapshot, o:Overlay; outputs: s':Snapshot; precondition: (* * The given Overlay is in the given Snapshot *) o in s.overlays; postcondition: (* * The Overlay is in the output Snapshot's list if and only if it is not * the given Overlay, and it is in the input Snapshot's list. * Additionally, all Overlays preceding the input Overlay in the list * remain in their same position, and all Overlays after the input Overlay * in the list are moved up one position. *) #(s'.overlays) = #(s.overlays) - 1 and (* * Copy over the first n - 1 Overlays from the input Snapshot to the * output Snapshot *) (forall (i:integer | (i >= 1) and (i < #(s.overlays) - 1)) s'.overlays[i] = s.overlays[i]) and (* * Loop through again and find the input Overlay. Once it is found, loop * from then onward, coping the i + 1 element of the input Snapshot's * list to the ith element of the output Snapshot's list. * PROF FISHER: Care to help me combine these two operations? Seems like * I'm doing this wrong. *) (forall (i:integer | (i >= 1) and (i < #(s.overlays))) if (s.overlays[i] = o) then ( forall (j:integer | (j >= i) and (j < #(s.overlays) - 1)) s'.overlays[j] = s.overlays[j + 1] ) ) ; end DeleteOverlay; obj StickyNote inherits from Sticky components: noteText:string; description: (* A sticky note is a virtual note that stores text (noteText) and can be linked to a Topic. *); end StickyNote; operation CreateStickyNote is inputs: dd:DeskDrawer, sn:StickyNote; outputs: dd':DeskDrawer; description: (* Adds a sticky note with the NoteText to the DeskDrawer. *); precondition: (* * None *) ; postcondition: (* * The created StickyNote is in the ouput DeskDrawer's StickyNote collection *) sn in dd'.notes; end CreateStickyNote; operation DeleteStickyNote is inputs: dd:DeskDrawer, sn:StickyNote; outputs: dd':DeskDrawer; description: (* Removes the given sticky note from the DeskDrawer's StickyNote collection. *); precondition: (* * The given StickyNote is in the input DeskDrawer's note collection *) sn in dd.notes; postcondition: (* * A StickyNote is in the output DeskDrawer's note collection if and only * if the StickyNote is not the given StickyNote and the StickyNote is in * the input DeskDrawer's note collection *) forall (sn':StickyNote) (sn' in dd'.notes) iff ((sn' != sn) and (sn' in dd.notes)); end DeleteStickyNote; operation LinkStickyNote is inputs: t:Topic, sn:StickyNote; outputs: t': Topic, sn':StickyNote; description: (* Links the StickyNote to the specified topic. *); precondition: (* * None *) ; postcondition: (* * A StickyNote is in the output Topic's stickies list if and only * if it is in the input Topic's stickies list or it is the input * StickyNote *) forall (note:StickyNote) (note in t'.stickies) iff (note in t.stickies or note = sn) and (* * The ouput StickyNote's statusText is set to the first string of * content within the Topic, if one exists *) forall (contentText:Text | contentText in t'.content.contentList) if (sn'.statusText = "Unlinked") then sn'.statusText = contentText and (* * If no contentText was set, set it to the Topic's enum. *) sn'.statusText = t.enum; end LinkStickyNote; operation UnlinkStickyNote is inputs: t:Topic, sn:StickyNote; outputs: t':Topic, sn':StickyNote; description: (* Removes the linking between the StickyNote and its linked Topic, if any exists. *); precondition: (* * The input StickyNote is in the input Topic's list of Stickys. *) sn in t.stickies; postcondition: (* * A StickyNote is in the output Topic's list if and only if it is in the * the input Topic's list and it is not the input StickyNote. *) forall (note:StickyNote) (note in t'.stickies) iff (note in t.stickies and note != sn) and (* * The output StickyNote's statusText is set to "Unlinked" *) sn'.statusText = "Unlinked"; end UnlinkStickyNote; end DeskDrawer;