/// <summary> /// Creates a new <see cref="ViewerPresentationLayout"/> instance and instantiates all of the child controls. /// </summary> /// <param name="model">The model whose <see cref="WorkspaceModel.CurrentDeckTraversal"/> property will /// be used to display the current slide and deck.</param> public ViewerPresentationLayout(PresenterModel model) { this.Name = "ViewerPresentationLayout"; this.m_Model = model; this.m_EventQueue = new ControlEventQueue(this); m_Model.Workspace.CurrentDeckTraversal.ListenAndInitialize(m_EventQueue, new Property<DeckTraversalModel>.EventHandler(this.HandleTraversalChanged)); // Create the film strip, docked to the right side of the container. this.m_DeckStrip = new DeckStrip(this.m_Model); /// Filmstrip docking this.m_DeckStrip.Dock = DockStyle.Right; // Make the deck strip resizable with a LinkedSplitter. // The splitter automatically sets its dock to be the same as the FilmStrip. this.m_DeckStripSplitter = new LinkedSplitter(this.m_DeckStrip); // Create the MainSlideViewer, which occupies the remaining space in the container. this.m_MainSlideViewer = new MainSlideViewer(this.m_Model, true); this.m_MainSlideViewer.Dock = DockStyle.Fill; #region RealTimeStylus Initialization // Create a new RealTimeStylus, which will process ink drawn on the MainSlideViewer. this.m_RealTimeStylus = new RealTimeStylus(this.m_MainSlideViewer, true); // Make sure the TransformableDynamicRenderer and InkAnnotationCollector // find out whenever the current StylusModel changes. this.m_StylusInputSelector = new StylusInputSelector(this.m_Model, this.m_RealTimeStylus); this.m_RealTimeStylus.SyncPluginCollection.Add(this.m_StylusInputSelector); // Scale the ink to the inverse of the MainSlideViewer's transform. // This keeps the ink's coordinates correct when the MainSlideViewer's transform // is applied again later by the TransformableDynamicRenderer and InkAnnotationCollector. this.m_InkTransformFilter = new InkTransformFilter(this.m_MainSlideViewer.SlideDisplay); this.m_RealTimeStylus.SyncPluginCollection.Add(this.m_InkTransformFilter); // Create a *synchronous* TransformableDynamicRenderer, which will render ink received directly // from the RealTimeStylus on a high-priority thread. this.m_TransformableDynamicRenderer = new TransformableDynamicRendererLinkedToDisplay(this.m_MainSlideViewer, this.m_MainSlideViewer.SlideDisplay); this.m_TransformableDynamicRenderer.Enabled = true; this.m_RealTimeStylus.SyncPluginCollection.Add(this.m_TransformableDynamicRenderer); // Don't dynamically render ink on the main slide viewer twice. The MainSlideViewer's RealTimeInkSheetRenderer's // own TransformableDynamicRenderer would render the ink on a low-priority asynchronous thread, which is // fine for secondary slide viewers and the FilmStrip, but not fine for the MainSlideViewer. using (Synchronizer.Lock(this.m_MainSlideViewer.SlideDisplay.SyncRoot)) { this.m_MainSlideViewer.SlideDisplay.RenderLocalRealTimeInk = false; } // Create an InkAnnotationCollector and wrap it in an InkSheetAdapter, // which sends ink from the RealTimeStylus to the RealTimeInkSheetModel // of the MainSlideViewer's current slide. this.m_InkAnnotationCollector = new InkAnnotationCollector(); this.m_InkAnnotationCollector_InkSheetAdapter = new InkSheetAdapter(this.m_MainSlideViewer.SlideDisplay, this.m_InkAnnotationCollector); this.m_RealTimeStylus.AsyncPluginCollection.Add(this.m_InkAnnotationCollector); this.m_LassoPlugin = new LassoPlugin(this.m_MainSlideViewer, this.m_MainSlideViewer.SlideDisplay); this.m_LassoPlugin_InkSheetAdapter = new InkSheetAdapter(this.m_MainSlideViewer.SlideDisplay, this.m_LassoPlugin); this.m_RealTimeStylus.SyncPluginCollection.Add(this.m_LassoPlugin); this.m_EraserPlugin = new EraserPlugin(this.m_MainSlideViewer.SlideDisplay); this.m_EraserPlugin_InkSheetAdapter = new InkSheetAdapter(this.m_MainSlideViewer.SlideDisplay, this.m_EraserPlugin); this.m_RealTimeStylus.SyncPluginCollection.Add(this.m_EraserPlugin); // Now that all the plugins have been added, enable the RealTimeStylus. this.m_RealTimeStylus.Enabled = true; #endregion RealTimeStylus Initialization // Create a DeckTraversalModelAdapter, which causes the MainSlideViewer to always display the // current slide of the current deck of the PresenterModel's current DeckTraversalModel. this.m_WorkspaceModelAdapter = new WorkspaceModelAdapter(this.m_MainSlideViewer.SlideDisplay.EventQueue, this.m_MainSlideViewer, this.m_Model); // Create the Slide Preview control this.m_SlidePreview = new SlidePreview(this.m_Model, this.m_DeckStripSplitter); this.m_PreviewTraversalModelAdapter = new PreviewTraversalModelAdapter(this.m_SlidePreview.m_EventQueue, this.m_SlidePreview.m_PreviewSlideViewer, this.m_Model); // Create the Second Monitor Form, this displays the slide on the secondary display this.m_SecondMonitorForm = new SecondMonitorForm(this.m_Model); this.ParentChanged += new EventHandler(OnParentChanged); // Create the Role Synchronizer for the MainSlideViewer this.m_RoleSync = new RoleSynchronizer(this.m_MainSlideViewer, this.m_Model.Participant); #region Add Controls // Add the SlidePreview Control this.Controls.Add(this.m_SlidePreview); // Now, add the controls in reverse order of their docking priority. // The MainSlideViewer gets added first so its Fill dock-style will be effective. this.Controls.Add(this.m_MainSlideViewer); // Next, dock the FilmStripSplitter and the FilmStrip in reverse order, // so that the FilmStripSplitter will be farther away from the edge of the container. this.Controls.Add(this.m_DeckStripSplitter); this.Controls.Add(this.m_DeckStrip); #endregion Add Controls }
protected override object SetUpMember(int index, object member) { TableOfContentsModel.Entry entry = ((TableOfContentsModel.Entry)member); FilmStripSlideViewer viewer; using (Synchronizer.Lock(entry.SyncRoot)) { // Only deal with entries that are direct children of the TableOfContentsModel. // Deeper entries are dealt with by the FilmStripSlideViewers. if (entry.Parent != null) return null; } Debug.Assert(this.m_Owner.IsHandleCreated); Debug.Assert(!this.m_Owner.InvokeRequired); this.m_Owner.SuspendLayout(); viewer = new FilmStripSlideViewer( this.m_Owner, this.m_Owner.m_Model, this.m_Owner.m_DeckTraversal, this.m_Owner.m_PreviewDeckTraversal, entry); viewer.MyMouseDown += new MouseEventHandler(this.m_Owner.HandleMouseDown); viewer.MouseUp += new MouseEventHandler(this.m_Owner.HandleMouseUp); viewer.MouseEnter += new EventHandler(this.m_Owner.HandleSlideEnter); viewer.MouseLeave += new EventHandler(this.m_Owner.HandleSlideLeave); viewer.MouseMove += new MouseEventHandler(this.m_Owner.HandleSlideMove); try { // Set the entry's Z-order. The control at index 0 will be docked farthest // away from the edge, so the "last" entry should be at index 0. // Unfortunately we don't have a guarantee that all of the TableOfContentsModel's // entries have yet been added to the FilmStrip (though that should be the case), // so we just go through (starting with z-order 0, the last thing in the filmstrip) // until we find the first entry whose index is less than ours. #if DEBUG Debug.Assert(index >= 0); using (Synchronizer.Lock(entry.TableOfContents.SyncRoot)) { Debug.Assert(entry.TableOfContents.Entries.Count > index); } #endif int zOrder = 0; using (Synchronizer.Lock(entry.SyncRoot)) { int i; for (i = 0; i < this.m_Owner.Controls.Count; i++) { TableOfContentsModel.Entry en = ((FilmStripSlideViewer)(this.m_Owner.Controls[i])).m_Entry; if (entry.TableOfContents.Entries.IndexOf(en) < entry.TableOfContents.Entries.IndexOf(entry)) { zOrder = Math.Max(0, i); i = this.m_Owner.Controls.Count + 1; } } if (i == this.m_Owner.Controls.Count) { zOrder = i; } } this.m_Owner.Controls.Add(viewer); this.m_Owner.Controls.SetChildIndex(viewer, zOrder); } catch { viewer.MyMouseDown -= new MouseEventHandler(this.m_Owner.HandleMouseDown); viewer.MouseUp -= new MouseEventHandler(this.m_Owner.HandleMouseUp); viewer.MouseEnter -= new EventHandler(this.m_Owner.HandleSlideEnter); viewer.MouseLeave -= new EventHandler(this.m_Owner.HandleSlideLeave); viewer.MouseMove -= new MouseEventHandler(this.m_Owner.HandleSlideMove); this.m_Owner.Controls.Remove(viewer); viewer.Dispose(); throw; } this.m_Owner.ResumeLayout(); RoleSynchronizer rs = new RoleSynchronizer(viewer, this.m_Owner.m_Model.Participant); return rs; }