/// <summary> /// This method steps through each of the stop conditions of the current slide. /// If any of them matches the current state, check for /// response correctness and set bChangeStimulus=true; /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> for which the slide should be parsed. /// </param> /// <param name="changeSlide"> /// Out. <strong>True</strong> if new slide should be shown. /// </param> /// <param name="response"> /// Out. The <see cref="StopCondition"/> that ended the slide. /// </param> private void CheckResponses( SlidePresentationContainer slideContainer, out bool changeSlide, out StopCondition response) { changeSlide = false; response = null; foreach (StopCondition condition in slideContainer.Slide.StopConditions) { if (condition is MouseStopCondition) { var msc = (MouseStopCondition)condition; if ((msc.CanBeAnyInputOfThisType && this.currentMousebutton != MouseButtons.None) || (this.currentMousebutton == msc.StopMouseButton)) { foreach (VGElement shape in slideContainer.Slide.TargetShapes) { if (shape.Contains(this.PointToClient(MousePosition))) { response = new MouseStopCondition(msc.StopMouseButton, false, shape.Name, null, MousePosition); if (msc.Target != string.Empty && (shape.Name == msc.Target || msc.Target == "Any")) { changeSlide = true; } break; } } if (msc.Target == string.Empty) { changeSlide = true; if (response == null) { response = new MouseStopCondition(msc.StopMouseButton, false, string.Empty, null, MousePosition); } } if (changeSlide) { // Check testing condition if specified. foreach (StopCondition correctCondition in slideContainer.Slide.CorrectResponses) { if (msc.Equals(correctCondition)) { response.IsCorrectResponse = true; break; } response.IsCorrectResponse = false; } this.currentMousebutton = MouseButtons.None; } } } else if (condition is KeyStopCondition) { var ksc = (KeyStopCondition)condition; if ((ksc.CanBeAnyInputOfThisType && this.currentKey != Keys.None) || (this.currentKey == ksc.StopKey)) { changeSlide = true; response = new KeyStopCondition(ksc.StopKey, false, null); // Check testing condition if specified. if (slideContainer.Slide.CorrectResponses != null) { // Check testing condition if specified. foreach (StopCondition correctCondition in slideContainer.Slide.CorrectResponses) { if (ksc.Equals(correctCondition)) { response.IsCorrectResponse = true; break; } response.IsCorrectResponse = false; } } this.currentKey = Keys.None; break; } } } }
/// <summary> /// This method scans the <see cref="SlidePresentationContainer.ElementsWithAudioOnClick"/> /// for an element that was clicked and plays it if it was the case. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> to search for audio files. /// </param> /// <param name="point"> /// A <see cref="Point"/> with the click location. /// </param> /// <param name="eventTime"> /// A <see cref="Int64"/> with the timestamp of the mouse click. /// </param> private void CheckforAudioStimulusOnClick(SlidePresentationContainer slideContainer, Point point, long eventTime) { foreach (VGElement element in slideContainer.ElementsWithAudioOnClick) { if (element.Contains(point)) { slideContainer.AudioPlayer.AddAudioChannel(element.Sound.FullFilename); if (slideContainer.AudioPlayer.PlayState != PlayState.Running) { slideContainer.AudioPlayer.Play(); } var soundEvent = new MediaEvent(); soundEvent.Type = EventType.Audio; soundEvent.Task = MediaEventTask.Start; soundEvent.Param = Path.GetFileName(element.Sound.Filename); this.OnTrialEventOccured(new TrialEventOccuredEventArgs(soundEvent, eventTime)); break; } } }
/// <summary> /// This method parses the given <see cref="VGElement"/> /// for sound files and fills them in the <see cref="OgamaControls.AudioPlayer"/>. /// If they should be played on click, they are stored in the /// <see cref="SlidePresentationContainer.ElementsWithAudioOnClick"/> list. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// this element belongs to. /// </param> /// <param name="element"> /// The <see cref="VGElement"/> to search for audio content /// </param> private static void ParseElementForAudio(SlidePresentationContainer slideContainer, VGElement element) { if (element.Sound != null && element.Sound.ShouldPlay) { if (!element.Sound.ShowOnClick) { slideContainer.AudioPlayer.AddAudioChannel(element.Sound.FullFilename); } else { slideContainer.ElementsWithAudioOnClick.Add(element); } } }
/// <summary> /// This method checks the <see cref="Slide.Links"/> collection, /// if the given response is in it, if so it sets the newTrialID /// ouput parameter, otherwise it will be -1. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> for which to check the links for. /// </param> /// <param name="response"> /// A <see cref="StopCondition"/> with the current response. /// </param> /// <param name="isLink"> /// Out. <strong>True</strong> if this response is a link, otherwise <strong>false</strong>. /// </param> /// <param name="newTrialID"> /// Out. An <see cref="Int32"/> with the new trial ID to link to. /// </param> private void CheckLinks( SlidePresentationContainer slideContainer, StopCondition response, out bool isLink, out int newTrialID) { isLink = false; newTrialID = -1; foreach (StopCondition condition in slideContainer.Slide.Links) { if (condition is MouseStopCondition && response is MouseStopCondition) { var linkMsc = (MouseStopCondition)condition; var responseMsc = (MouseStopCondition)response; if (linkMsc.StopMouseButton == responseMsc.StopMouseButton && linkMsc.Target == responseMsc.Target) { if (linkMsc.TrialID != null) { newTrialID = linkMsc.TrialID.Value; } isLink = true; break; } } else if (condition is KeyStopCondition && response is KeyStopCondition) { var linkKsc = (KeyStopCondition)condition; var responseKsc = (KeyStopCondition)response; if (linkKsc.StopKey == responseKsc.StopKey) { if (linkKsc.TrialID != null) { newTrialID = linkKsc.TrialID.Value; } isLink = true; break; } } } }
/// <summary> /// This method setups the mouse cursor of the new slide. /// Its sets the position and visibility of the cursor. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// that should be initialized. /// </param> private void SetupMouse(SlidePresentationContainer slideContainer) { // Show or hide mouse cursor at specified position. if (slideContainer.Slide.MouseCursorVisible) { // Reset the cursor position to initial location if applicable if (slideContainer.Slide.ForceMousePositionChange) { var newPoint = new Point( slideContainer.Slide.MouseInitialPosition.X + this.presentationBounds.Left, slideContainer.Slide.MouseInitialPosition.Y + this.presentationBounds.Top); Cursor.Position = newPoint; } if (this.hiddenCursor) { Cursor.Show(); this.hiddenCursor = false; } } else { if (!this.hiddenCursor) { Cursor.Hide(); this.hiddenCursor = true; } } }
/// <summary> /// Initializes a new instance of the PresenterModule class. /// </summary> public PresenterModule() { this.InitializeComponent(); this.nfi = new CultureInfo("en-US", false).NumberFormat; this.nfi.NumberGroupSeparator = string.Empty; // Retrieves the BufferedGraphicsContext for the // current application domain. this.context = BufferedGraphicsManager.Current; // Sets the maximum size for the primary graphics buffer // of the buffered graphics context for the application // domain. Any allocation requests for a buffer larger // than this will create a temporary buffered graphics // context to host the graphics buffer. this.context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1); this.preparedSlideOne = new SlidePresentationContainer { ContainerControl = this.panelOne }; // Allocates a graphics buffer the size of this form // using the pixel format of the Graphics created by // the Form.CreateGraphics() method, which returns a // Graphics object that matches the pixel format of the form. var width = Document.ActiveDocument.ExperimentSettings.WidthStimulusScreen; var height = Document.ActiveDocument.ExperimentSettings.HeightStimulusScreen; this.preparedSlideOne.DrawingSurface = this.context.Allocate( this.panelOne.CreateGraphics(), new Rectangle(0, 0, width, height)); this.panelOne.DrawingSurface = this.preparedSlideOne.DrawingSurface; this.preparedSlideTwo = new SlidePresentationContainer(); this.preparedSlideTwo.ContainerControl = this.panelTwo; this.preparedSlideTwo.DrawingSurface = this.context.Allocate( this.panelTwo.CreateGraphics(), new Rectangle(0, 0, width, height)); this.panelTwo.DrawingSurface = this.preparedSlideTwo.DrawingSurface; this.keyboardCallback = this.KeyboardHookCallback; this.mouseCallback = this.MouseHookCallback; }
/// <summary> /// This method swaps the slide buffers by swapping the /// position of the controls panelOne and panelTwo and /// updating the shownSlide. /// </summary> private void PresentPreparedSlide() { // Detach scroll event listeners if (this.shownSlideContainer != null) { foreach (VGElement element in this.shownSlideContainer.Slide.ActiveXStimuli) { if (element is VGBrowser) { var browser = element as VGBrowser; try { browser.WebBrowser.NewWindow -= this.WebBrowser_NewWindow; if (browser.WebBrowser.Document != null) { if (browser.WebBrowser.Document.Window != null) { // browser.WebBrowser.Document.Window.Scroll -= this.WebBrowserScroll; } browser.WebBrowser.DocumentCompleted -= this.WebBrowserDocumentCompleted; browser.WebBrowser.Navigating -= this.WebBrowserNavigating; browser.WebBrowser.Document.MouseDown -= this.WebBrowserMouseDown; } } catch (NullReferenceException ex) { ExceptionMethods.HandleExceptionSilent(ex); } } } } switch (this.shownContainer) { case ShownContainer.One: this.Controls.SetChildIndex(this.panelTwo, 0); this.shownSlideContainer = this.preparedSlideTwo; this.shownContainer = ShownContainer.Two; break; case ShownContainer.None: case ShownContainer.Two: this.Controls.SetChildIndex(this.panelOne, 0); this.shownSlideContainer = this.preparedSlideOne; this.shownContainer = ShownContainer.One; break; } // Care for desktop recording slides var slidePresentationContainer = this.shownSlideContainer; if (slidePresentationContainer != null && slidePresentationContainer.Slide.IsDesktopSlide) { this.WindowState = FormWindowState.Minimized; this.keyboardHookID = MessageHook.SetKeyboardHook(this.keyboardCallback); this.mouseHookID = MessageHook.SetMouseHook(this.mouseCallback); } else { this.WindowState = FormWindowState.Maximized; } // Reset response fields this.currentMousebutton = MouseButtons.None; this.currentKey = Keys.None; // Need a refresh here because otherwise the video // capture will be started before the screen has been redrawn // and it would start with a frame of the old // slide. this.Refresh(); if (this.shownSlideContainer.Timer.Period > 1) { this.shownSlideContainer.Timer.Start(); } }
/// <summary> /// This method sends trigger signals to the ports if /// triggering is enabled in the module. /// First it sends the general trigger, and the the slide trigger, if /// it is enabled. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> that should send the slide trigger. /// </param> /// <remarks> /// This method is called in a separate thread, because /// the signaling time could be long lasting. /// </remarks> private void SendTrigger(SlidePresentationContainer slideContainer) { try { if (this.enableTrigger) { // Send general trigger if applicable if (this.generalTrigger.Signaling != TriggerSignaling.None) { this.generalTrigger.Send(); } // Send Slide trigger if applicable if (this.generalTrigger.Signaling != TriggerSignaling.Override) { if (slideContainer.Slide.TriggerSignal.Signaling == TriggerSignaling.Enabled) { slideContainer.Slide.TriggerSignal.Send(); } } } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
/// <summary> /// This method is called whenever the slide has been swapped to the /// foreground and its replay of audio streams and /// optional screen capturing should be started. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// that should be initiated for replay. /// </param> private void PlaySlideContainer(SlidePresentationContainer slideContainer) { if (slideContainer.AudioPlayer.PlayState != PlayState.Running) { slideContainer.AudioPlayer.Play(); } var countFlash = 0; if (this.shownSlideContainer.Slide.HasActiveXContent) { foreach (VGElement element in slideContainer.Slide.ActiveXStimuli) { if (element is VGFlash) { var flash = element as VGFlash; flash.SendMessagesToParent(true); flash.Play(); countFlash++; } else if (element is VGBrowser) { var browser = element as VGBrowser; if (browser.WebBrowser.IsDisposed) { throw new ObjectDisposedException("Browser of new slide is already disposed..."); } browser.SendMessagesToParent(true); if (browser.WebBrowser.InvokeRequired) { Application.DoEvents(); } while (browser.WebBrowser.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); if (this.closing) { return; } } // Set input focus to the webbrowser, otherwise the // mouse will not act like a webbrowser mouse showing a // hand on a link etc. browser.WebBrowser.Focus(); } } } // Just do screen capturing on flash content or desktop recording if (countFlash > 0 || this.shownSlideContainer.Trial.HasDesktopRecordingContent) { if (this.screenCapture != null && !this.screenCapture.IsRunning) { this.screenCapture.Start(); } } }
/// <summary> /// This method parses the <see cref="Slide.VGStimuli"/> /// for elements with sound files and fills them in the <see cref="OgamaControls.AudioPlayer"/>. /// If they should be played on click, they are stored in the /// <see cref="SlidePresentationContainer.ElementsWithAudioOnClick"/> list. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// to pe parsed for audio content that should be prepared for replay. /// </param> private void ParseElementsForAudio(SlidePresentationContainer slideContainer) { slideContainer.ElementsWithAudioOnClick.Clear(); foreach (VGElement element in slideContainer.Slide.VGStimuli) { ParseElementForAudio(slideContainer, element); } foreach (VGElement element in slideContainer.Slide.ActiveXStimuli) { ParseElementForAudio(slideContainer, element); } }
/// <summary> /// This method initializes the next slide in the given /// <see cref="SlidePresentationContainer"/>. /// That is setting a time stop condition timer, /// setup audio replay, and load flash objects. /// Because this can last a significant amount of time /// it should be done in a background thread. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// that should be initialized. /// </param> private void InitializeNextSlide(SlidePresentationContainer slideContainer) { try { // Reset Timer to 1ms period indicating that it should not be used during // call to PlaySlideContainer slideContainer.Timer.Period = 1; // Reset the timer according to stop condition if applicable. foreach (StopCondition condition in slideContainer.Slide.StopConditions) { if (condition is TimeStopCondition) { var timeCondition = (TimeStopCondition)condition; slideContainer.Timer.Period = timeCondition.Duration; break; } } // Search for audio files this.ParseElementsForAudio(slideContainer); // Load background sound if (slideContainer.Slide.BackgroundSound != null) { if (slideContainer.Slide.BackgroundSound.ShouldPlay) { slideContainer.AudioPlayer.AddAudioChannel(slideContainer.Slide.BackgroundSound.FullFilename); } } // Check for flash stimuli and load them into the // flashObject activeX control. foreach (VGElement element in slideContainer.Slide.ActiveXStimuli) { if (element is VGFlash) { var flash = element as VGFlash; flash.InitializeOnControl(slideContainer.ContainerControl, true, new System.Drawing.Drawing2D.Matrix()); } else if (element is VGBrowser) { var browser = element as VGBrowser; this.currentVgBrowser = browser; browser.InitializeOnControl(slideContainer.ContainerControl, true); browser.WebBrowser.NewWindow += this.WebBrowser_NewWindow; browser.WebBrowser.DocumentCompleted += this.WebBrowserDocumentCompleted; browser.WebBrowser.Navigating += this.WebBrowserNavigating; // if (browser.WebBrowser.Url != null && browser.WebBrowser.Document != null) // { // // Attach Scroll events in document completed handler // browser.WebBrowser.Document.MouseDown += this.WebBrowserMouseDown; // } browser.WebBrowser.Navigate(browser.BrowserURL); this.numberOfTimesNavigated = 0; this.maxBrowseDepth = browser.BrowseDepth; this.currentBrowserTreeNode = (BrowserTreeNode)Document.ActiveDocument.ExperimentSettings.SlideShow.GetNodeByID(slideContainer.Trial.ID); } } } catch (Exception ex) { ExceptionMethods.HandleExceptionSilent(ex); } }
/// <summary> /// This method asynchronously calls the drawing method of the slide of /// the given container to the given slidecontainers drawing surface. /// </summary> /// <param name="slideToDraw"> /// The <see cref="SlidePresentationContainer"/> /// whichs slide should be drawn to its buffer. /// </param> private void DrawToBuffer(SlidePresentationContainer slideToDraw) { if (slideToDraw.Slide.PresentationSize == Size.Empty) { slideToDraw.Slide.PresentationSize = Document.ActiveDocument.PresentationSize; } // Draw slides contents. this.DrawSlideAsyncMethod(slideToDraw.Slide, slideToDraw.DrawingSurface.Graphics); }
/// <summary> /// This method releases the resources used in the given /// <see cref="SlidePresentationContainer"/> but not all. /// Some items should not be disposed because they are reused for /// the next slide. /// </summary> /// <param name="slideContainer"> /// The <see cref="SlidePresentationContainer"/> /// to be disposed and prepared for next use. /// </param> private void DisposeSlideContainer(SlidePresentationContainer slideContainer) { // Explicitely dispose flash objects // otherwise we will get an exception from // the MDA reportAvOnComRelease if (slideContainer.ContainerControl.Controls.Count > 0) { foreach (Control ctrl in slideContainer.ContainerControl.Controls) { if (ctrl is AxFlashControl) { if (ctrl.InvokeRequired) { MethodInvoker ctrlDisposeDelegate = ctrl.Dispose; ctrl.Invoke(ctrlDisposeDelegate); } else { ctrl.Dispose(); } } else if (ctrl is WebBrowser) { if (ctrl.InvokeRequired) { MethodInvoker ctrlDisposeDelegate = ctrl.Dispose; ctrl.Invoke(ctrlDisposeDelegate); } } } } foreach (VGElement element in slideContainer.Slide.ActiveXStimuli) { if (element is VGFlash) { var flash = element as VGFlash; flash.SendMessagesToParent(false); } else if (element is VGBrowser) { var browser = element as VGBrowser; browser.SendMessagesToParent(false); } } if (this.InvokeRequired) { MethodInvoker controlsClearMethod = slideContainer.ContainerControl.Controls.Clear; this.Invoke(controlsClearMethod); } else { slideContainer.ContainerControl.Controls.Clear(); } slideContainer.Slide.Dispose(); // Stop audio playback and release player slideContainer.AudioPlayer.CloseAudioFile(); slideContainer.ElementsWithAudioOnClick.Clear(); // Stop current running timer. if (slideContainer.Timer.IsRunning) { slideContainer.Timer.Stop(); } }