private void HandleEntriesChangedHelper() { using (Synchronizer.Lock(this.SyncRoot)) { using (Synchronizer.Lock(this.Deck.TableOfContents.SyncRoot)) { if (this.Current == null) { // We don't have a current entry to start from, so start from the beginning. this.Reset(); } if (this.Current != null && !this.m_Filter(this.Current)) { // The current entry is no longer be accepted by the filter. // Find the next entry relative to the current, or the previous entry if there is no next. TableOfContentsModel.Entry replacement = this.FindNext(this.Current); this.Current = (replacement != null) ? replacement : this.FindPrevious(this.Current); // Setting the current entry will have updated the Next and Previous properties, // so there's nothing more to do. return; } // Update the Next and Previous entries, in case the current Next or Previous // entries changed so that they are no longer accepted by the filter. // TODO: Only try to update Next and Previous if the changed entries are adjacent // to the current entry or one of its parents. This may be beneficial if the // tree is very broad. this.UpdateEntries(); } } }
/// <summary> /// Sets the <see cref="Current">current entry</see> to the first applicable entry /// in the deck's table of contents, or <c>null</c> if the table of contents is empty. /// </summary> /// <remarks> /// The caller is required to aquire a reader lock on the <see cref="DeckModel#TableOfContents"/>. /// </remarks> private void Reset() { if (this.Deck.TableOfContents.Entries.Count > 0) { TableOfContentsModel.Entry entry = this.Deck.TableOfContents[0]; this.Current = this.m_Filter(entry) ? entry : this.FindNext(entry); } }
public static DeckModel OpenXPS(string file) { try { Misc.ProgressBarForm progressForm = new UW.ClassroomPresenter.Misc.ProgressBarForm("Opening \"" + file + "\"..."); progressForm.Show(); //Open Xps Document XpsDocument xpsDocument = null; xpsDocument = new XpsDocument(file, FileAccess.Read); //Create two DocumentPaginators for the XPS document. One is for local display, the other one is for the delivery to students. DocumentPaginator paginator = xpsDocument.GetFixedDocumentSequence().DocumentPaginator; //Create Deck Model Guid deckGuid = Guid.NewGuid(); DeckModel deck = new DeckModel(deckGuid, DeckDisposition.Empty, file); using (Synchronizer.Lock(deck.SyncRoot)) { deck.IsXpsDeck = true; //Iterate over all pages for (int i = 0; i < paginator.PageCount; i++) { DocumentPage page = paginator.GetPage(i); SlideModel newSlideModel = CreateSlide(page, deck); //Create a new Entry + reference SlideModel TableOfContentsModel.Entry newEntry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, newSlideModel); //Lock the TOC using (Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { //Add Entry to TOC deck.TableOfContents.Entries.Add(newEntry); } //Update the Progress Bar progressForm.UpdateProgress(0, paginator.PageCount , i+1); } } //Close Progress Bar progressForm.Close(); return deck; } catch (Exception e) { System.Diagnostics.Trace.WriteLine(e.ToString()); } GC.Collect(); return null; }
public FilmStripSlideViewer(MultiColumnFilmStrip parent, PresenterModel model, DeckTraversalModel traversal, DeckTraversalModel previewTraversal, TableOfContentsModel.Entry entry) { this.m_Model = model; this.m_Parent = parent; this.m_DeckTraversal = traversal; this.m_Entry = entry; this.m_PreviewDeckTraversal = previewTraversal; this.BorderStyle = BorderStyle.FixedSingle; this.m_CurrentEntryChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.SlideDisplay.EventQueue, new PropertyEventHandler(this.HandleCurrentEntryChanged)); this.m_EntriesChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.SlideDisplay.EventQueue, new PropertyEventHandler(this.HandleEntriesChanged)); this.m_DeckBackgroundColorChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.SlideDisplay.EventQueue, new PropertyEventHandler(this.HandleDeckBackgroundColorChanged)); this.m_DeckBackgroundTemplateChangedDispatcher = new EventQueue.PropertyEventDispatcher(this.SlideDisplay.EventQueue, new PropertyEventHandler(this.HandleDeckBackgroundTemplateChanged)); this.m_DeckTraversal.Changed["Current"].Add(this.m_CurrentEntryChangedDispatcher.Dispatcher); this.m_Entry.TableOfContents.Changed["Entries"].Add(this.m_EntriesChangedDispatcher.Dispatcher); using (Synchronizer.Lock(this.m_DeckTraversal.Deck.SyncRoot)) { this.m_DeckTraversal.Deck.Changed["DeckBackgroundColor"].Add(this.m_DeckBackgroundColorChangedDispatcher.Dispatcher); this.m_DeckTraversal.Deck.Changed["DeckBackgroundTemplate"].Add(this.m_DeckBackgroundTemplateChangedDispatcher.Dispatcher); this.DefaultDeckBGColor = this.m_DeckTraversal.Deck.DeckBackgroundColor; this.DefaultDeckBGTemplate = this.m_DeckTraversal.Deck.DeckBackgroundTemplate; } this.m_EntriesChangedDispatcher.Dispatcher(this.m_Entry, null); this.m_CurrentEntryChangedDispatcher.Dispatcher(this.m_DeckTraversal, null); this.m_CurrentDeckTraversalChangedDispatcher = this.m_Model.Workspace.CurrentDeckTraversal.ListenAndInitialize(this.SlideDisplay.EventQueue, delegate(Property<DeckTraversalModel>.EventArgs args) { bool old = this.m_IsCurrentDeckTraversal; using (this.m_Model.Workspace.Lock()) { this.m_IsCurrentDeckTraversal = (~this.m_Model.Workspace.CurrentDeckTraversal == this.m_DeckTraversal); } if (old != this.m_IsCurrentDeckTraversal) { // Invalidate the display to draw the new selection border. this.UpdateSelection(); } }); }
public SlideModel(Guid id, LocalId localId, SlideDisposition disposition, Rectangle bounds, Guid associationId) : this(id, localId, disposition, bounds) { this.m_AssociationId = associationId; //If this is a student submission, the associationID should be set. In this case we //gather some extra information about the association slide and deck. if (!m_AssociationId.Equals(Guid.Empty)) { PresentationModel pres = PresentationModel.CurrentPresentation; if (pres != null) { PresentationModel.DeckTraversalCollection traversals; traversals = pres.DeckTraversals; foreach (DeckTraversalModel dtm in traversals) { DeckModel deck; using (Synchronizer.Lock(dtm.SyncRoot)) { deck = dtm.Deck; } Guid deckId; DeckDisposition deckDisp; TableOfContentsModel toc; using (Synchronizer.Lock(deck.SyncRoot)) { deckId = deck.Id; deckDisp = deck.Disposition; toc = deck.TableOfContents; } TableOfContentsModel.Entry tocEntry = toc.GetEntryBySlideId(m_AssociationId); if (tocEntry != null) { using (Synchronizer.Lock(tocEntry.SyncRoot)) { m_AssociationSlideIndex = tocEntry.IndexInParent; } m_AssociationDeckDispostion = deckDisp; m_AssociationDeckId = deckId; break; } } } } }
private void InsertSlide() { ///get the index of the current slide that's selected, ///so that we can insert our blank slide there int current_slide_index; using (Synchronizer.Lock(traversal_.SyncRoot)) { current_slide_index = traversal_.AbsoluteCurrentSlideIndex; } ///if current_slide_index == -1, this means there are no slides in current deck. ///This new slide we try to insert is the first slide. if (current_slide_index < 0) { current_slide_index = 0; } // Create a blank slide SlideModel slide; using (Synchronizer.Lock(this.m_Deck.SyncRoot)) { slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS); this.m_Deck.Dirty = true; this.m_Deck.InsertSlide(slide); } ///Insert our blank slide after the current index. This is modeled after the powerpoint ///UI using (Synchronizer.Lock(this.m_Deck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.m_Deck.TableOfContents, slide); this.m_WhereTheEntriesGo.Insert(current_slide_index, entry); } }
private void InsertDeck(FileInfo file) { ///get the index of the current slide that's selected, ///so that we can insert our deck there int current_slide_index; using (Synchronizer.Lock(deck_traversal_.SyncRoot)) { current_slide_index = deck_traversal_.AbsoluteCurrentSlideIndex; } ///if current_slide_index == -1, this means there are no slides in current deck. ///This new slide we try to insert is the first slide. if (current_slide_index < 0) { current_slide_index = 0; } /// get the deck of slides to insert DeckModel deck = null; if (file.Extension == ".cp3") { deck = Decks.PPTDeckIO.OpenCP3(file); } else if (file.Extension == ".ppt") { deck = Decks.PPTDeckIO.OpenPPT(file); } else if (file.Extension == ".pptx") { deck = Decks.PPTDeckIO.OpenPPT(file); } else if (file.Extension == ".xps") { deck = Decks.XPSDeckIO.OpenXPS(file); } if (deck == null) { // This should never happen, since we have filtered on extension throw new Exception("Wrong file extension"); } else { deck_.InsertDeck(deck); } using (Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { using (Synchronizer.Lock(deck_.TableOfContents.SyncRoot)) { ///insert each slide from the deck into our current deck. for (int i = 0; i < deck.TableOfContents.Entries.Count; i++) { ///create our TOC entry TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck_.TableOfContents, deck.TableOfContents.Entries[i].Slide); ///insert it into our TOC this.m_WhereTheEntriesGo.Insert(current_slide_index + i, entry);//Natalie - should it be +1? } } } }
private TableOfContentsModel.Entry insertImageHelper(int index, string filename) { //Assume that traversal is already locked! using(Synchronizer.Lock(this.deck.SyncRoot)) { using(Synchronizer.Lock(this.deck.TableOfContents.SyncRoot)) { //Create a new Background layer //Compute the MD5 of the BG FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); MD5 md5Provider = new MD5CryptoServiceProvider(); byte[] md5 = md5Provider.ComputeHash(fs); // fs.Seek(0, SeekOrigin.Begin); // Image imageFile = Image.FromStream(fs); // fs.Close(); fs.Close(); Image imageFile; try { imageFile = Image.FromFile(filename); } catch (Exception e) { MessageBox.Show("Error reading in file:\r\n" + filename + "\r\n" + e.Message); imageFile = new Bitmap(10, 10); } //Create a new SlideModel SlideModel newSlideModel = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, new Rectangle(new Point(0, 0), imageFile.Size)); using(Synchronizer.Lock(newSlideModel.SyncRoot)) { //Set the slide's title newSlideModel.Title = filename; //Create the ImageSheet ImageSheetModel sheet = new ImageSheetModel(deck, Guid.NewGuid(), Model.Presentation.SheetDisposition.Background, new Rectangle(new Point(0, 0), imageFile.Size), (ByteArray)md5, 1); //Add the ImageSheet to the Slide newSlideModel.ContentSheets.Add(sheet); TableOfContentsModel.Entry newEntry; //Add the Image+MD5 to the deck this.deck.AddSlideContent((ByteArray)md5, imageFile); //Add SlideModel to the deck this.deck.InsertSlide(newSlideModel); //Create a new Entry + reference SlideModel newEntry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, newSlideModel); //Add Entry to TOC this.deck.TableOfContents.Entries.Insert(index, newEntry); this.isDirty = true; //Return the entry so we can navigate to it, if we wish return newEntry; } } } }
private void DuplicateSlide() { ///get the index of the current slide that's selected, ///so that we can insert our blank slide there int current_slide_index; using (Synchronizer.Lock(traversal_.SyncRoot)) { current_slide_index = traversal_.AbsoluteCurrentSlideIndex - 1; } ///don't do anything if no object is selected if (current_slide_index < 0) { return; } // Copy slide SlideModel newSlide; using (Synchronizer.Lock(this.m_Deck.SyncRoot)) { SlideModel currentSlide = this.m_Deck.TableOfContents.Entries[current_slide_index].Slide; newSlide = CloneSlideAndSheets(currentSlide); //remove remote disposition for student submission slide; The removal may be not necessary for non-student submission slide. //If current role is non-instructor,the sheet models in slide should contain remote disposition if ((currentSlide.Disposition & SlideDisposition.StudentSubmission)!=0) { newSlide.RemoveRemoteDisposition(); } this.m_Deck.Dirty = true; this.m_Deck.InsertSlide(newSlide); } ///Insert our current slide after the current index. This is modeled after the powerpoint ///UI using (Synchronizer.Lock(this.m_Deck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.m_Deck.TableOfContents, newSlide); this.m_WhereTheEntriesGo.Insert(current_slide_index + 1, entry); } }
/// <summary> /// Construct the matching between two entries /// </summary> /// <param name="srcEntry">The source entry</param> /// <param name="dstEntry">The destination entry</param> public TableOfContentsEntryMatch( TableOfContentsModel.Entry srcEntry, TableOfContentsModel.Entry dstEntry ) { this.m_SourceEntry = srcEntry; this.m_DestEntry = dstEntry; }
protected void LocalAddStudentSubmission( Guid g, Guid TOCEntryGuid ) { using( Synchronizer.Lock( this.m_Slide.SyncRoot ) ) { // Only create student submissions from slides that exist on the client machine SlideModel newSlide = new SlideModel( g, new LocalId(), SlideDisposition.Remote, new Rectangle(0,0,0,0) ); // Update the fields of the slide using( Synchronizer.Lock( newSlide.SyncRoot ) ) { using( Synchronizer.Lock( this.m_Slide.SyncRoot ) ) { newSlide.Title = this.m_Slide.Title; newSlide.Bounds = new Rectangle( this.m_Slide.Bounds.Location, this.m_Slide.Bounds.Size ); newSlide.Zoom = this.m_Slide.Zoom; newSlide.BackgroundColor = this.m_Slide.BackgroundColor; newSlide.BackgroundTemplate = this.m_Slide.BackgroundTemplate; newSlide.SubmissionSlideGuid = this.m_Slide.SubmissionSlideGuid; newSlide.SubmissionStyle = this.m_Slide.SubmissionStyle; // TODO CMPRINCE: Copy all the sheets over (Do a deep copy?) foreach( SheetModel s in this.m_Slide.ContentSheets ) { newSlide.ContentSheets.Add( s ); } // Make a deep copy of all the ink sheets foreach( SheetModel s in this.m_Slide.AnnotationSheets ) { newSlide.AnnotationSheets.Add( UW.ClassroomPresenter.Model.Presentation.SheetModel.SheetDeepCopyHelper( s ) ); } } } // Add the slide to the deck DeckModel ssDeck = GetPresentationStudentSubmissionsDeck(); if( ssDeck != null ) { // Add the slide itself using( Synchronizer.Lock( ssDeck.SyncRoot ) ) { ssDeck.InsertSlide( newSlide ); Message.AddLocalRef(newSlide.Id, newSlide); } // Add an entry to the deck traversal so that we can navigate to the slide using( Synchronizer.Lock( ssDeck.TableOfContents.SyncRoot ) ) { TableOfContentsModel.Entry e = new TableOfContentsModel.Entry( TOCEntryGuid, ssDeck.TableOfContents, newSlide ); ssDeck.TableOfContents.Entries.Add( e ); Message.AddLocalRef(e.Id, e); } } } }
private static bool TestEntry(TableOfContentsModel.Entry entry) { return(entry.Slide != null); }
/// <summary> /// Constructor /// </summary> /// <param name="i">The index of this entry</param> /// <param name="e">The entry this control represents</param> /// <param name="deckColor">The background color for the deck /// containing this entry</param> /// <param name="location">The location for this control</param> /// <param name="size">The size of this control</param> public SSOrganizerEntry(int i, TableOfContentsModel.Entry e, Color deckColor, PointF location, SizeF size) { this.entry = e; this.location = location; this.size = size; this.SuspendLayout(); // Create the background image this.cachedImage = this.CacheSlideImage(deckColor); this.BackgroundImage = this.cachedImage; this.BackgroundImageLayout = ImageLayout.Stretch; this.BackColor = Color.White; this.BorderStyle = BorderStyle.FixedSingle; this.ClientSize = new Size(200, 150); this.Location = new Point(i * this.Size.Width, 0); this.Anchor = AnchorStyles.Left | AnchorStyles.Top; this.ResumeLayout(); }
/// <summary> /// Finds the next entry, in pre-order relative to the specified entry, which is accepted by the entry filter. /// </summary> /// <remarks> /// The caller is required to hold a reader lock on the entry's <see cref="TableOfContentsModel"/>. /// </remarks> public TableOfContentsModel.Entry FindNext(TableOfContentsModel.Entry entry) { if (entry == null) { return(null); } using (Synchronizer.Lock(entry.SyncRoot)) { // Pre-order traversal: we've already visited the "current" entry, so visit it's children next. if (entry.Children.Count > 0) { TableOfContentsModel.Entry child = entry.Children[0]; if (this.m_Filter(child)) { return(child); } else { return(this.FindNext(child)); } } // If there are no children, look for a next sibling, parent's sibling, etc. else { TableOfContentsModel.Entry parent = entry.Parent; TableOfContentsModel.EntryCollection siblings = (parent == null) ? entry.TableOfContents.Entries : parent.Children; while (siblings != null) { int index = siblings.IndexOf(entry); // Use the entry's sibling within the parent, if possible. if (index >= 0 && index < siblings.Count - 1) { TableOfContentsModel.Entry sibling = siblings[index + 1]; if (this.m_Filter(sibling)) { return(sibling); } else { return(this.FindNext(sibling)); } } // If there is no sibling in the parent, go up another level. // Do not consider parent nodes in the pre-order traversal. else { entry = parent; parent = (entry == null) ? null : entry.Parent; siblings = (parent == null) ? null : parent.Children; } } // If we get here, then we've run out of parents and there is no next entry! return(null); } } }
/// <summary> /// Finds the next entry, relative to the specified entry, which is accepted by the entry filter. /// </summary> /// <remarks> /// The caller is required to hold a reader lock on the entry's <see cref="TableOfContentsModel"/>. /// </remarks> public TableOfContentsModel.Entry FindPrevious(TableOfContentsModel.Entry entry) { if (entry == null) { return(null); } // Pre-order traversal: we start with the last descendent of the entry's previous sibling. using (Synchronizer.Lock(entry.SyncRoot)) { TableOfContentsModel.Entry parent = entry.Parent; TableOfContentsModel.EntryCollection siblings = (parent == null) ? entry.TableOfContents.Entries : parent.Children; int index = siblings.IndexOf(entry); // If there exists a previous sibling, we're in good shape. // (This also handles the case when the given entry is not a child of its parent, // which can happen when entries are received over the network in a strange order.) if (index > 0) { entry = siblings[index - 1]; // Find the *last* descendent of the sibling by going down as far as possible. siblings = entry.Children; while (siblings.Count > 0) { entry = siblings[siblings.Count - 1]; siblings = entry.Children; } // Now, we've either got the original sibling or the last descendent of it. // Use this as the previous entry, or start here if it's not accepted by the filter. if (this.m_Filter(entry)) { return(entry); } else { return(this.FindPrevious(entry)); } } // If there's no previous sibling and no parent, we're screwed. else if (parent == null) { return(null); } // Otherwise, if there's no previous sibling, use the parent. else { if (this.m_Filter(parent)) { return(parent); } else { return(this.FindPrevious(parent)); } } } }
private void insertBlankSlide() { //If we have nothing open, create a new deck and traversal bool newDeck = this.blankDeckPreCheck(); using(Synchronizer.Lock(this.traversal.SyncRoot)) { using(Synchronizer.Lock(this.deck.SyncRoot)) { using(Synchronizer.Lock(this.deck.TableOfContents.SyncRoot)) { //TODO: The slide bounds should not be hardcoded... SlideModel newSlideModel = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, new Rectangle(0, 0, 720, 540)); this.deck.InsertSlide(newSlideModel); TableOfContentsModel.Entry newEntry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.deck.TableOfContents, newSlideModel); if (newDeck) { this.deck.TableOfContents.Entries.Add(newEntry); } else { int index = this.traversal.AbsoluteCurrentSlideIndex; if (this.insertBefore) { index--; } this.deck.TableOfContents.Entries.Insert(index, newEntry); } //Navigate to that blank slide this.traversal.Current = newEntry; } } } this.isDirty = true; //Add event listeners for new decks this.blankDeckPostCheck(newDeck); }
protected void SetPrevious(TableOfContentsModel.Entry value) { this.SetPublishedProperty("Previous", ref this.m_Previous, value); }
protected void SetNext(TableOfContentsModel.Entry value) { this.SetPublishedProperty("Next", ref this.m_Next, value); }
private void CopySlideToPublicDeck() { ///get the index of the current slide that's selected, ///so that we can insert our blank slide there int current_slide_index; using (Synchronizer.Lock(traversal_.SyncRoot)) { current_slide_index = traversal_.AbsoluteCurrentSlideIndex - 1; } ///don't do anything if no object is selected if (current_slide_index < 0) { return; } DeckModel publicDeck = GetPublicSubmissionDeck(); // Copy slide SlideModel newSlide; using (Synchronizer.Lock(this.m_Deck.SyncRoot)) { SlideModel currentSlide = this.m_Deck.TableOfContents.Entries[current_slide_index].Slide; newSlide = CloneSlideAndSheets(currentSlide); using (Synchronizer.Lock(newSlide.SyncRoot)) { using (Synchronizer.Lock(currentSlide.SyncRoot)) { if (currentSlide.BackgroundTemplate == null) newSlide.BackgroundTemplate = this.m_Deck.DeckBackgroundTemplate; if (currentSlide.BackgroundColor == Color.Empty) newSlide.BackgroundColor = this.m_Deck.DeckBackgroundColor; } } newSlide.RemoveRemoteDisposition(); } using (Synchronizer.Lock(publicDeck.SyncRoot)) { publicDeck.Dirty = true; publicDeck.InsertSlide(newSlide); UW.ClassroomPresenter.Network.Messages.Message.AddLocalRef(newSlide.Id, newSlide); } using (Synchronizer.Lock(publicDeck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry e = new TableOfContentsModel.Entry(Guid.NewGuid(), publicDeck.TableOfContents, newSlide); publicDeck.TableOfContents.Entries.Add(e); UW.ClassroomPresenter.Network.Messages.Message.AddLocalRef(e.Id, e); } ///Insert our current slide after the current index. This is modeled after the powerpoint ///UI // using (Synchronizer.Lock(this.m_Deck.TableOfContents.SyncRoot)) { // TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.m_Deck.TableOfContents, newSlide); // this.m_WhereTheEntriesGo.Insert(current_slide_index + 1, entry); // } }
//TODO: Image type needs to be more dynamically chosen... public static DeckModel OpenPPT(FileInfo file, BackgroundWorker worker, DoWorkEventArgs progress) { //Start the progress bar if (worker != null) { worker.ReportProgress(0, " Initializing..."); } //Make the default flat tree representation of the PPT //Try to detect if powerpoint is already running (powerpnt.exe) bool pptAlreadyRunning = false; Process[] processes = Process.GetProcesses(); for (int i = 0; i < processes.Length; i++) { string currentProcess = processes[i].ProcessName.ToLower(); if (currentProcess == "powerpnt") { pptAlreadyRunning = true; break; } } //Open PowerPoint + open file PowerPoint.Application pptapp = null; try { pptapp = new PowerPoint.Application(); } catch (Exception e) { throw new PPTNotInstalledException("Failed to create PowerPoint Application. See InnerException for details.", e); } PowerPoint._Presentation presentation; try { presentation = pptapp.Presentations.Open(file.FullName, Core.MsoTriState.msoTrue, Core.MsoTriState.msoFalse, Core.MsoTriState.msoFalse); } catch (Exception e) { throw new PPTFileOpenException("Failed to open PowerPoint file. See InnerException for details.", e); } //Initialize the PPT Shape tag reader PPTPaneManagement.PPTPaneManager pptpm = new PPTPaneManagement.PPTPaneManager(); //Create a new DeckModel Guid deckGuid = Guid.Empty; try { string g = presentation.Tags["WEBEXPORTGUID"]; if (g == "") { deckGuid = Guid.NewGuid(); } else { deckGuid = new Guid(g); } } catch { deckGuid = Guid.NewGuid(); } DeckModel deck = new DeckModel(deckGuid, DeckDisposition.Empty, file.Name); //Initialize a temporary file collection that will be where slide images are exported to TempFileCollection tempFileCollection = new TempFileCollection(); string dirpath = tempFileCollection.BasePath; if (!Directory.Exists(dirpath)) { Directory.CreateDirectory(dirpath); } else { Directory.Delete(dirpath, true); Directory.CreateDirectory(dirpath); } //Lock it using(Synchronizer.Lock(deck.SyncRoot)) { //Iterate over all slides for (int i = 1; i <= presentation.Slides.Count; i++) { if (progress != null && progress.Cancel) break; //Get the slide PowerPoint._Slide currentSlide= presentation.Slides[i]; if (currentSlide.SlideShowTransition.Hidden == Core.MsoTriState.msoTrue) continue; SlideModel newSlideModel = CreateSlide(presentation.PageSetup, pptpm, deck, tempFileCollection, dirpath, currentSlide); //Create a new Entry + reference SlideModel TableOfContentsModel.Entry newEntry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, newSlideModel); //Lock the TOC using(Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { //Add Entry to TOC deck.TableOfContents.Entries.Add(newEntry); } //Increment the ProgressBarForm if (worker != null) { worker.ReportProgress((i * 100) / presentation.Slides.Count, " Reading slide " + i + " of " + presentation.Slides.Count); } } } //Close the presentation presentation.Close(); presentation = null; //If PowerPoint was not open before, close PowerPoint if (!pptAlreadyRunning) { pptapp.Quit(); pptapp = null; } GC.Collect(); //Delete temp directory tempFileCollection.Delete(); Directory.Delete(dirpath); if (worker != null) worker.ReportProgress(100, " Done!"); //Return the deck if (progress != null) progress.Result = deck; return deck; }
/// <summary> /// /// </summary> /// <param name="server"></param> /// <param name="deckIndex"></param> /// <param name="slideIndex"></param> /// <param name="strokes">SimpleWebInk objects that make up the strokes.</param> public void HandleStudentSubmission( object server, int deckIndex, int slideIndex, ArrayList strokes ) { SlideModel slide = null; DeckModel deck = null; using (Synchronizer.Lock(this.m_Presentation.SyncRoot)) { using (Synchronizer.Lock(this.m_Presentation.DeckTraversals[deckIndex].SyncRoot)) { using( Synchronizer.Lock(this.m_Presentation.DeckTraversals[deckIndex].Current.SyncRoot) ) { // Get the slide model slide = this.m_Presentation.DeckTraversals[deckIndex].Current.Slide; } deck = this.m_Presentation.DeckTraversals[deckIndex].Deck; } } // Get the student submissions deck or create it if it doesn't exist DeckModel ssDeck = this.m_Presentation.GetStudentSubmissionDeck(); if (ssDeck == null) { // Create the student submissions deck Guid ssGuid = new Guid("{78696D29-AA11-4c5b-BCF8-8E6406077FD4}"); Guid ssTraversalGuid = new Guid("{4884044B-DAE1-4249-AEF2-3A2304F52E97}"); ssDeck = new DeckModel(ssGuid, DeckDisposition.StudentSubmission, "Student Submissions"); ssDeck.Group = Group.Submissions; ssDeck.current_subs = true; // AddLocalRef(ssGuid, ssDeck); DeckTraversalModel traversal = new SlideDeckTraversalModel(ssTraversalGuid, ssDeck); // AddLocalRef(ssTraversalGuid, traversal); // Add the new student submission deck to the presentation using (Synchronizer.Lock(this.m_Presentation.SyncRoot)) { this.m_Presentation.DeckTraversals.Add(traversal); } } // Create the new slide to add SlideModel newSlide = new SlideModel( new Guid(), new LocalId(), SlideDisposition.Remote | SlideDisposition.StudentSubmission ); // Make a list of image content sheets that need to be added to the deck. List<ImageSheetModel> images = new List<ImageSheetModel>(); // Update the fields of the slide using (Synchronizer.Lock(newSlide.SyncRoot)) { using (Synchronizer.Lock(slide.SyncRoot)) { newSlide.Title = slide.Title; newSlide.Bounds = slide.Bounds; newSlide.Zoom = slide.Zoom; newSlide.BackgroundColor = slide.BackgroundColor; newSlide.BackgroundTemplate = slide.BackgroundTemplate; newSlide.SubmissionSlideGuid = slide.SubmissionSlideGuid; newSlide.SubmissionStyle = slide.SubmissionStyle; //If the slide background is null, then update the slide background with deck setting using (Synchronizer.Lock(deck.SyncRoot)) { if (slide.BackgroundColor == System.Drawing.Color.Empty) { newSlide.BackgroundColor = deck.DeckBackgroundColor; } if (slide.BackgroundTemplate == null) { newSlide.BackgroundTemplate = deck.DeckBackgroundTemplate; } } // Copy all of the content sheets. // Because ContentSheets do not change, there is no // need to do a deep copy (special case for ImageSheetModels). foreach (SheetModel s in slide.ContentSheets) { newSlide.ContentSheets.Add(s); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if (ism != null) images.Add(ism); } // Make a deep copy of all the ink sheets foreach (SheetModel s in slide.AnnotationSheets) { SheetModel newSheet = UW.ClassroomPresenter.Model.Presentation.SheetModel.SheetDeepRemoteCopyHelper(s); newSlide.AnnotationSheets.Add(newSheet); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if (ism != null) images.Add(ism); } } } // Add the slide content to the deck. using (Synchronizer.Lock(ssDeck.SyncRoot)) { foreach (ImageSheetModel ism in images) { System.Drawing.Image image = ism.Image; if (image == null) using (Synchronizer.Lock(ism.Deck.SyncRoot)) using (Synchronizer.Lock(ism.SyncRoot)) image = ism.Deck.GetSlideContent(ism.MD5); if (image != null) ssDeck.AddSlideContent(ism.MD5, image); } // Add the slide to the deck. ssDeck.InsertSlide(newSlide); } // Add an entry to the deck traversal so that we can navigate to the slide using (Synchronizer.Lock(ssDeck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry e = new TableOfContentsModel.Entry( new Guid(), ssDeck.TableOfContents, newSlide); ssDeck.TableOfContents.Entries.Add(e); } // Add the ink to the slide now using (Synchronizer.Lock(newSlide.SyncRoot)) { RealTimeInkSheetModel sheet = new RealTimeInkSheetModel(new Guid(), SheetDisposition.All | SheetDisposition.Remote, newSlide.Bounds); // Add the sheet newSlide.AnnotationSheets.Add(sheet); // Now add the ink using (Synchronizer.Lock(sheet.Ink.Strokes.SyncRoot)) { foreach (SimpleWebInk stroke in strokes) { Microsoft.Ink.Stroke s = sheet.Ink.CreateStroke(stroke.Pts); s.DrawingAttributes.Color = System.Drawing.Color.FromArgb(stroke.R, stroke.G, stroke.B); s.DrawingAttributes.RasterOperation = (stroke.Opacity < 255) ? Microsoft.Ink.RasterOperation.MaskPen : Microsoft.Ink.RasterOperation.CopyPen; s.DrawingAttributes.Width = stroke.Width * 30.00f; } } } }
public SlideMenuItem(TableOfContentsModel.Entry entry, DeckTraversalModel traversal, int slideCount) { this.Entry = entry; this.Traversal = traversal; using (Synchronizer.Lock(this.Entry.Slide.SyncRoot)) { //Set a 50 char limit on the title string toDisplay = this.Entry.Slide.Title; if (toDisplay.Length > 50) { toDisplay = toDisplay.Substring(0, 50) + "..."; } this.Text = "" + slideCount + ". " + toDisplay; } using (Synchronizer.Lock(this.Traversal)) { this.Traversal.Changed["Current"].Add(new Model.PropertyEventHandler(this.OnUpdate)); } this.OnUpdate(this, null); }
protected override void OnClick(EventArgs e) { base.OnClick(e); // Change the name of whiteboard according to the number of it string wbname = "WhiteBoard "+ (++UW.ClassroomPresenter.Viewer.ViewerForm.white_board_num).ToString(); // NOTE: This code is duplicated in DeckNavigationToolBarButtons.WhiteboardToolBarButton and DeckMessage.UpdateContext DeckModel deck = new DeckModel(Guid.NewGuid(), DeckDisposition.Whiteboard, wbname); using (Synchronizer.Lock(deck.SyncRoot)) { SlideModel slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS); deck.InsertSlide(slide); using (Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, slide); deck.TableOfContents.Entries.Add(entry); } } DeckTraversalModel traversal = new SlideDeckTraversalModel(Guid.NewGuid(), deck); using (this.m_Model.Workspace.Lock()) { if ((~this.m_Model.Workspace.CurrentPresentation) != null) { using (Synchronizer.Lock((~this.m_Model.Workspace.CurrentPresentation).SyncRoot)) { (~this.m_Model.Workspace.CurrentPresentation).DeckTraversals.Add(traversal); } } else { this.m_Model.Workspace.DeckTraversals.Add(traversal); } } }
/// <summary> /// Handle the receipt of this message /// </summary> /// <param name="context">The context of the receiver from which the message was sent</param> protected override bool UpdateTarget(ReceiveContext context) { #if DEBUG // Add logging of slide change events string machine_name = ""; string machine_guid = ""; using( Synchronizer.Lock( context.Participant.SyncRoot ) ) { machine_name = context.Participant.HumanName; machine_guid = context.Participant.Guid.ToString(); } Debug.WriteLine( string.Format( "SUBMISSION RCVD ({0}): Machine -- {1}, GUID -- {2}", System.DateTime.Now.Ticks, machine_name, machine_guid ) ); #endif SlideModel slide = this.Target as SlideModel; // Only create student submissions from slides that exist on the client machine // TODO CMPRINCE: Should we create these anyway??? if (slide != null) { // Get the student submissions deck DeckModel ssDeck = GetPresentationStudentSubmissionsDeck(); if (ssDeck != null) { // Check if this entry already exists using (Synchronizer.Lock(ssDeck.TableOfContents.SyncRoot)) { foreach (TableOfContentsModel.Entry ent in ssDeck.TableOfContents.Entries) { if (ent.Id == this.TOCEntryGuid) { this.Target = ent.Slide; return false; } } } // Create the new slide to add SlideModel newSlide = new SlideModel(this.SlideGuid, this.LocalId, SlideDisposition.Remote | SlideDisposition.StudentSubmission, this.Bounds, (Guid)this.TargetId); this.Target = newSlide; // Make a list of image content sheets that need to be added to the deck. List<ImageSheetModel> images = new List<ImageSheetModel>(); // Update the fields of the slide using (Synchronizer.Lock(newSlide.SyncRoot)) { using (Synchronizer.Lock(slide.SyncRoot)) { newSlide.Title = this.Title; newSlide.Bounds = this.Bounds; newSlide.Zoom = this.Zoom; newSlide.BackgroundColor = this.SlideBackgroundColor; newSlide.SubmissionSlideGuid = this.SubmissionSlideGuid; newSlide.SubmissionStyle = this.SubmissionStyle; // Copy all of the content sheets. // Because ContentSheets do not change, there is no // need to do a deep copy (special case for ImageSheetModels). foreach (SheetModel s in slide.ContentSheets) { newSlide.ContentSheets.Add(s); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if (ism != null) images.Add(ism); } // Make a deep copy of all the ink sheets foreach (SheetModel s in slide.AnnotationSheets) { SheetModel newSheet = InkSheetDeepCopyHelper(s); newSlide.AnnotationSheets.Add(newSheet); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if (ism != null) images.Add(ism); } } } using (Synchronizer.Lock(ssDeck.SyncRoot)) { // Add the slide content to the deck. foreach (ImageSheetModel ism in images) { Image image = ism.Image; if (image == null) using (Synchronizer.Lock(ism.Deck.SyncRoot)) using (Synchronizer.Lock(ism.SyncRoot)) image = ism.Deck.GetSlideContent(ism.MD5); if (image != null) ssDeck.AddSlideContent(ism.MD5, image); } // Add the slide to the deck. ssDeck.InsertSlide(newSlide); AddLocalRef(newSlide.Id, newSlide); } // Add an entry to the deck traversal so that we can navigate to the slide using (Synchronizer.Lock(ssDeck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry e = new TableOfContentsModel.Entry(this.TOCEntryGuid, ssDeck.TableOfContents, newSlide); ssDeck.TableOfContents.Entries.Add(e); AddLocalRef(e.Id, e); } } } // Fix Bug 803: "Erased ink and text still shows up in student submissions". // We've updated our target with 'newSheet', which is NOT the real target! // Child messages should be able to access this target, but we don't want to // save the 'newSheet' in the targets table. We want to keep the original sheet. return false; }
/// <summary> /// Creates a new QuickPoll and the associated slide (and deck) /// </summary> /// <param name="model">The PresenterModel</param> /// <param name="role">The RoleModel</param> public static void CreateNewQuickPoll( PresenterModel model, RoleModel role ) { // Create the quickpoll QuickPollModel newQuickPoll = null; using( Synchronizer.Lock( model.ViewerState.SyncRoot ) ) { newQuickPoll = new QuickPollModel( Guid.NewGuid(), Guid.NewGuid(), model.ViewerState.PollStyle ); } // Add a new QuickPoll to the model // NOTE: This should trigger a network message about the new QuickPoll // NOTE: Need to do this first before adding the sheet otherwise the // public display will not be able to associate the sheet with // this quick poll. using( model.Workspace.Lock() ) { using( Synchronizer.Lock( (~model.Workspace.CurrentPresentation).SyncRoot ) ) { (~model.Workspace.CurrentPresentation).QuickPoll = newQuickPoll; } } // Add the quickpoll slide to the quickpoll using( model.Workspace.Lock() ) { using( Synchronizer.Lock( model.Participant.SyncRoot ) ) { DeckTraversalModel qpTraversal = null; DeckModel qpDeck = null; // Find the first quickpoll slidedeck. foreach( DeckTraversalModel candidate in model.Workspace.DeckTraversals ) { if( (candidate.Deck.Disposition & DeckDisposition.QuickPoll) != 0 ) { qpTraversal = candidate; using( Synchronizer.Lock( qpTraversal.SyncRoot ) ) { qpDeck = qpTraversal.Deck; } break; } } // If there is no existing quickpoll deck, create one. if( qpTraversal == null ) { // Change the name of quickpoll according to the number of it string qpName = "QuickPoll"; // NOTE: This code is duplicated in DecksMenu.CreateBlankWhiteboardDeckMenuItem. qpDeck = new DeckModel( Guid.NewGuid(), DeckDisposition.QuickPoll, qpName ); qpDeck.Group = Network.Groups.Group.Submissions; qpTraversal = new SlideDeckTraversalModel( Guid.NewGuid(), qpDeck ); if( model.Workspace.CurrentPresentation.Value != null ) { using( Synchronizer.Lock( (~model.Workspace.CurrentPresentation).SyncRoot ) ) { (~model.Workspace.CurrentPresentation).DeckTraversals.Add( qpTraversal ); } } else { model.Workspace.DeckTraversals.Add( qpTraversal ); } } // Add the slide // TODO CMPRINCE: Associate the quickpoll with this slide using( Synchronizer.Lock( qpDeck.SyncRoot ) ) { // Get the Current Slide SlideModel oldSlide; using( Synchronizer.Lock( role.SyncRoot ) ) { using( Synchronizer.Lock( ((InstructorModel)role).CurrentDeckTraversal.SyncRoot ) ) { using( Synchronizer.Lock( ((InstructorModel)role).CurrentDeckTraversal.Current.SyncRoot ) ) { oldSlide = ((InstructorModel)role).CurrentDeckTraversal.Current.Slide; } } } // Copy the values and sheets from the old slide to the new slide using( Synchronizer.Lock( oldSlide.SyncRoot ) ) { // Create the new slide to add SlideModel newSlide = new SlideModel( Guid.NewGuid(), new LocalId(), SlideDisposition.Remote | SlideDisposition.StudentSubmission, DEFAULT_SLIDE_BOUNDS, oldSlide.Id ); // Make a list of image content sheets that need to be added to the deck. List<ImageSheetModel> images = new List<ImageSheetModel>(); // Update the fields of the slide using( Synchronizer.Lock( newSlide.SyncRoot ) ) { newSlide.Title = oldSlide.Title; newSlide.Bounds = oldSlide.Bounds; newSlide.Zoom = oldSlide.Zoom; newSlide.BackgroundColor = oldSlide.BackgroundColor; newSlide.SubmissionSlideGuid = oldSlide.SubmissionSlideGuid; newSlide.SubmissionStyle = oldSlide.SubmissionStyle; // Copy all of the content sheets. // Because ContentSheets do not change, there is no // need to do a deep copy (special case for ImageSheetModels). foreach( SheetModel s in oldSlide.ContentSheets ) { newSlide.ContentSheets.Add( s ); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if( ism != null ) images.Add( ism ); } // Add the QuickPollSheet newSlide.ContentSheets.Add( new QuickPollSheetModel( Guid.NewGuid(), newQuickPoll ) ); // Make a deep copy of all the ink sheets foreach( SheetModel s in oldSlide.AnnotationSheets ) { SheetModel newSheet = InkSheetModel.InkSheetDeepCopyHelper( s ); newSlide.AnnotationSheets.Add( newSheet ); // Queue up any image content to be added the deck below. ImageSheetModel ism = s as ImageSheetModel; if( ism != null ) images.Add( ism ); } } // Add the slide content to the deck. foreach( ImageSheetModel ism in images ) { System.Drawing.Image image = ism.Image; if( image == null ) using( Synchronizer.Lock( ism.Deck.SyncRoot ) ) using( Synchronizer.Lock( ism.SyncRoot ) ) image = ism.Deck.GetSlideContent( ism.MD5 ); if( image != null ) qpDeck.AddSlideContent( ism.MD5, image ); } // Add the slide to the deck. qpDeck.InsertSlide( newSlide ); // Add an entry to the deck traversal so that we can navigate to the slide using( Synchronizer.Lock( qpDeck.TableOfContents.SyncRoot ) ) { TableOfContentsModel.Entry e = new TableOfContentsModel.Entry( Guid.NewGuid(), qpDeck.TableOfContents, newSlide ); qpDeck.TableOfContents.Entries.Add( e ); } } } } } }
protected override bool UpdateTarget(ReceiveContext context) { SlideModel slide = this.Parent != null ? this.Parent.Target as SlideModel : null; if(slide == null) return false; DeckModel deck = this.Parent.Parent != null ? this.Parent.Parent.Target as DeckModel : null; if(deck == null) return false; TableOfContentsModel.Entry entry = this.Target as TableOfContentsModel.Entry; if(entry == null) this.Target = entry = new TableOfContentsModel.Entry(((Guid) this.TargetId), deck.TableOfContents, slide); this.UpdatePathFromRoot(entry); return true; }
/// <summary> /// Advance to the next slide /// <remarks> /// Code taken from the next slide toolbar button /// </remarks> /// </summary> protected void NextSlide() { using( Synchronizer.Lock( this ) ) { if( this.currentDeckTraversal == null ) return; using( Synchronizer.Lock( this.currentDeckTraversal.SyncRoot ) ) { if( this.currentDeckTraversal.Next != null ) // Advance the slide this.currentDeckTraversal.Current = this.currentDeckTraversal.Next; else { // Add a whiteboard slide if we are at the end of the deck using (Synchronizer.Lock(this.currentDeckTraversal.Deck.SyncRoot)) { if (((this.currentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) != 0) && ((this.currentDeckTraversal.Deck.Disposition & DeckDisposition.Remote) == 0)) { // Add the new slide SlideModel slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS); this.currentDeckTraversal.Deck.InsertSlide(slide); using (Synchronizer.Lock(this.currentDeckTraversal.Deck.TableOfContents.SyncRoot)) { // Add the new table of contents entry TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), this.currentDeckTraversal.Deck.TableOfContents, slide); this.currentDeckTraversal.Deck.TableOfContents.Entries.Add(entry); // Move to the slide this.currentDeckTraversal.Current = entry; } } } } } } }
/// <summary> /// Handle clicking of this button (switch between whiteboard and normal deck) /// </summary> /// <param name="args">The event arguments</param> protected override void OnClick(EventArgs args) { using (this.m_Model.Workspace.Lock()) { using( Synchronizer.Lock( this.m_Model.Participant.SyncRoot ) ) { DeckTraversalModel traversal; if (this.CurrentDeckTraversal == null || (this.CurrentDeckTraversal.Deck.Disposition & DeckDisposition.Whiteboard) == 0) { traversal = this.m_PreviousWhiteboard; // Make sure the deck hasn't been closed. if (!this.m_Model.Workspace.DeckTraversals.Contains(traversal)) traversal = this.m_PreviousWhiteboard = null; // If there is no previous whiteboard traversal, find one or create one. if (traversal == null) { // TODO: If the participant is an instructor, *only* search the whiteboards in the active presentation. // Then, when changing the current deck, also change the current presentation if necessary. foreach (DeckTraversalModel candidate in this.m_Model.Workspace.DeckTraversals) { if ((candidate.Deck.Disposition & DeckDisposition.Whiteboard) != 0) { traversal = candidate; break; } } // If there is no existing whiteboard deck, create one. if (traversal == null) { // Change the name of whiteboard according to the number of it string wbname = "WhiteBoard " + (++UW.ClassroomPresenter.Viewer.ViewerForm.white_board_num).ToString(); // NOTE: This code is duplicated in DecksMenu.CreateBlankWhiteboardDeckMenuItem. DeckModel deck = new DeckModel(Guid.NewGuid(), DeckDisposition.Whiteboard, wbname); using (Synchronizer.Lock(deck.SyncRoot)) { SlideModel slide = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, UW.ClassroomPresenter.Viewer.ViewerForm.DEFAULT_SLIDE_BOUNDS); deck.InsertSlide(slide); using (Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { TableOfContentsModel.Entry entry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, slide); deck.TableOfContents.Entries.Add(entry); } } traversal = new SlideDeckTraversalModel(Guid.NewGuid(), deck); if (this.m_Model.Workspace.CurrentPresentation.Value != null) { using (Synchronizer.Lock((~this.m_Model.Workspace.CurrentPresentation).SyncRoot)) { (~this.m_Model.Workspace.CurrentPresentation).DeckTraversals.Add(traversal); } } else { this.m_Model.Workspace.DeckTraversals.Add(traversal); } } } } else { traversal = this.m_PreviousNormal; // Make sure the deck hasn't been closed. if (!this.m_Model.Workspace.DeckTraversals.Contains(traversal)) traversal = this.m_PreviousNormal = null; // Search the workspace for a non-whiteboard deck if we haven't encountered one before. if (traversal == null) { // TODO: If the participant is an instructor, *only* search the whiteboards in the active presentation. foreach (DeckTraversalModel candidate in this.m_Model.Workspace.DeckTraversals) { if ((candidate.Deck.Disposition & DeckDisposition.Whiteboard) == 0) { traversal = candidate; break; } } // If we still haven't found a non-whiteboard deck, do nothing. if (traversal == null) return; } } // If the user's role is an InstructorModel, set the InstructorModel's CurrentDeckTraversal. // The NetworkAssociationService will then use this to set the WorkspaceModel's CurrentDeckTraversal, // and the new deck traversal will also be broadcast. InstructorModel instructor = this.m_Model.Participant.Role as InstructorModel; if (instructor != null) { using (Synchronizer.Lock(instructor.SyncRoot)) { instructor.CurrentDeckTraversal = traversal; } } else { // Otherwise, we must set the CurrentDeckTraversal on the WorkspaceModel directly. using (this.m_Model.Workspace.Lock()) { this.m_Model.Workspace.CurrentDeckTraversal.Value = traversal; } } } } base.OnClick(args); }