private void HandleFunctionKeySelectionResult(KeyValue singleKeyValue)
        {
            if (singleKeyValue.FunctionKey != null)
            {
                keyStateService.ProgressKeyDownState(singleKeyValue);

                var currentKeyboard = Keyboard;

                switch (singleKeyValue.FunctionKey.Value)
                {
                    case FunctionKeys.AddToDictionary:
                        AddTextToDictionary();
                        break;

                    case FunctionKeys.AlphaKeyboard:
                        Log.Debug("Changing keyboard to Alpha.");
                        Keyboard = new Alpha();
                        break;

                    case FunctionKeys.BackFromKeyboard:
                        Log.Debug("Navigating back from keyboard.");
                        var navigableKeyboard = Keyboard as IBackAction;
                        if (navigableKeyboard != null && navigableKeyboard.BackAction != null)
                        {
                            navigableKeyboard.BackAction();
                        }
                        else
                        {
                            Keyboard = new Alpha();
                        }
                        break;

                    case FunctionKeys.Calibrate:
                        if (CalibrationService != null)
                        {
                            Log.Debug("Calibrate requested.");

                            var question = CalibrationService.CanBeCompletedWithoutManualIntervention
                                ? "Are you sure you would like to re-calibrate?"
                                : "Calibration cannot be completed without manual intervention, e.g. having to use a mouse. You may be stuck in the calibration process if you cannot manually interact with your computer.\nAre you sure you would like to re-calibrate?";

                            Keyboard = new YesNoQuestion(
                                question,
                                () =>
                                {
                                    inputService.RequestSuspend();
                                    Keyboard = currentKeyboard;
                                    CalibrateRequest.Raise(new NotificationWithCalibrationResult(), calibrationResult =>
                                    {
                                        if (calibrationResult.Success)
                                        {
                                            audioService.PlaySound(Settings.Default.InfoSoundFile, Settings.Default.InfoSoundVolume);
                                            RaiseToastNotification("Success", calibrationResult.Message, NotificationTypes.Normal, () => inputService.RequestResume());
                                        }
                                        else
                                        {
                                            audioService.PlaySound(Settings.Default.ErrorSoundFile, Settings.Default.ErrorSoundVolume);
                                            RaiseToastNotification("Uh-oh!", calibrationResult.Exception != null
                                                    ? calibrationResult.Exception.Message
                                                    : calibrationResult.Message ?? "Something went wrong, but I don't know what - please check the logs",
                                                NotificationTypes.Error,
                                                () => inputService.RequestResume());
                                        }
                                    });
                                },
                                () =>
                                {
                                    Keyboard = currentKeyboard;
                                });
                        }
                        break;

                    case FunctionKeys.CollapseDock:
                        Log.Debug("Collapsing dock.");
                        mainWindowManipulationService.ResizeDockToCollapsed();
                        if (Keyboard is ViewModels.Keyboards.Mouse)
                        {
                            Settings.Default.MouseKeyboardDockSize = DockSizes.Collapsed;
                        }
                        break;

                    case FunctionKeys.ConversationAlphaKeyboard:
                        Log.Debug("Changing keyboard to ConversationAlpha.");
                        var opacityBeforeConversationAlpha = mainWindowManipulationService.GetOpacity();
                        Action conversationAlphaBackAction =
                            currentKeyboard is ConversationNumericAndSymbols
                                ? ((ConversationNumericAndSymbols)currentKeyboard).BackAction
                                : () =>
                                    {
                                        Log.Debug("Restoring window size.");
                                        mainWindowManipulationService.Restore();
                                        Log.DebugFormat("Restoring window opacity to {0}", opacityBeforeConversationAlpha);
                                        mainWindowManipulationService.SetOpacity(opacityBeforeConversationAlpha);
                                        Keyboard = currentKeyboard;
                                    };
                        Keyboard = new ConversationAlpha(conversationAlphaBackAction);
                        Log.Debug("Maximising window.");
                        mainWindowManipulationService.Maximise();
                        Log.DebugFormat("Setting opacity to 1 (fully opaque)");
                        mainWindowManipulationService.SetOpacity(1);
                        break;

                    case FunctionKeys.ConversationNumericAndSymbolsKeyboard:
                        Log.Debug("Changing keyboard to ConversationNumericAndSymbols.");
                        var opacityBeforeConversationNumericAndSymbols = mainWindowManipulationService.GetOpacity();
                        Action conversationNumericAndSymbolsBackAction =
                            currentKeyboard is ConversationAlpha
                                ? ((ConversationAlpha)currentKeyboard).BackAction
                                : () =>
                                    {
                                        Log.Debug("Restoring window size.");
                                        mainWindowManipulationService.Restore();
                                        Log.DebugFormat("Restoring window opacity to {0}", opacityBeforeConversationNumericAndSymbols);
                                        mainWindowManipulationService.SetOpacity(opacityBeforeConversationNumericAndSymbols);
                                        Keyboard = currentKeyboard;
                                    };
                        Keyboard = new ConversationNumericAndSymbols(conversationNumericAndSymbolsBackAction);
                        Log.Debug("Maximising window.");
                        mainWindowManipulationService.Maximise();
                        Log.DebugFormat("Setting opacity to 1 (fully opaque)");
                        mainWindowManipulationService.SetOpacity(1);
                        break;

                    case FunctionKeys.Currencies1Keyboard:
                        Log.Debug("Changing keyboard to Currencies1.");
                        Keyboard = new Currencies1();
                        break;

                    case FunctionKeys.Currencies2Keyboard:
                        Log.Debug("Changing keyboard to Currencies2.");
                        Keyboard = new Currencies2();
                        break;

                    case FunctionKeys.DecreaseOpacity:
                        Log.Debug("Decreasing opacity.");
                        mainWindowManipulationService.IncrementOrDecrementOpacity(false);
                        break;

                    case FunctionKeys.Diacritic1Keyboard:
                        Log.Debug("Changing keyboard to Diacritic1.");
                        Keyboard = new Diacritics1();
                        break;

                    case FunctionKeys.Diacritic2Keyboard:
                        Log.Debug("Changing keyboard to Diacritic2.");
                        Keyboard = new Diacritics2();
                        break;

                    case FunctionKeys.Diacritic3Keyboard:
                        Log.Debug("Changing keyboard to Diacritic3.");
                        Keyboard = new Diacritics3();
                        break;

                    case FunctionKeys.ExpandDock:
                        Log.Debug("Expanding dock.");
                        mainWindowManipulationService.ResizeDockToFull();
                        if (Keyboard is ViewModels.Keyboards.Mouse)
                        {
                            Settings.Default.MouseKeyboardDockSize = DockSizes.Full;
                        }
                        break;

                    case FunctionKeys.ExpandToBottom:
                        Log.DebugFormat("Expanding to bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.Bottom, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToBottomAndLeft:
                        Log.DebugFormat("Expanding to bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.BottomLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToBottomAndRight:
                        Log.DebugFormat("Expanding to bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.BottomRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToLeft:
                        Log.DebugFormat("Expanding to left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.Left, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToRight:
                        Log.DebugFormat("Expanding to right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.Right, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTop:
                        Log.DebugFormat("Expanding to top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.Top, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTopAndLeft:
                        Log.DebugFormat("Expanding to top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.TopLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTopAndRight:
                        Log.DebugFormat("Expanding to top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Expand(ExpandToDirections.TopRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.IncreaseOpacity:
                        Log.Debug("Increasing opacity.");
                        mainWindowManipulationService.IncrementOrDecrementOpacity(true);
                        break;

                    case FunctionKeys.MenuKeyboard:
                        Log.Debug("Restoring window size.");
                        mainWindowManipulationService.Restore();
                        Log.Debug("Changing keyboard to Menu.");
                        Keyboard = new Menu(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.Minimise:
                        Log.Debug("Minimising window.");
                        mainWindowManipulationService.Minimise();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() =>
                        {
                            Log.Debug("Restoring window size.");
                            mainWindowManipulationService.Restore();
                            Keyboard = currentKeyboard;
                        });
                        break;

                    case FunctionKeys.MouseDrag:
                        Log.Debug("Mouse drag selected.");
                        SetupFinalClickAction(firstFinalPoint =>
                        {
                            if (firstFinalPoint != null)
                            {
                                audioService.PlaySound(Settings.Default.MouseDownSoundFile, Settings.Default.MouseDownSoundVolume);

                                //This class reacts to the point selection event AFTER the MagnifyPopup reacts to it.
                                //This means that if the MagnifyPopup sets the nextPointSelectionAction from the
                                //MagnifiedPointSelectionAction then it will be called immediately i.e. for the same point.
                                //The workaround is to set the nextPointSelectionAction to a lambda which sets the NEXT
                                //nextPointSelectionAction. This means the immediate call to the lambda just sets up the
                                //delegate for the subsequent call.
                                nextPointSelectionAction = repeatFirstClickOrSecondClickAction =>
                                {
                                    Action<Point> deferIfMagnifyingElseDoNow = repeatFirstClickOrSecondClickPoint =>
                                    {
                                        Action<Point?> secondFinalClickAction = secondFinalPoint =>
                                        {
                                            if (secondFinalPoint != null)
                                            {
                                                Action<Point, Point> simulateDrag = (fp1, fp2) =>
                                                {
                                                    Log.DebugFormat("Performing mouse drag between points ({0},{1}) and {2},{3}).", fp1.X, fp1.Y, fp2.X, fp2.Y);
                                                    mouseOutputService.MoveTo(fp1);
                                                    mouseOutputService.LeftButtonDown();
                                                    audioService.PlaySound(Settings.Default.MouseUpSoundFile, Settings.Default.MouseUpSoundVolume);
                                                    mouseOutputService.MoveTo(fp2);
                                                    mouseOutputService.LeftButtonUp();
                                                };

                                                lastMouseActionStateManager.LastMouseAction =
                                                    () => simulateDrag(firstFinalPoint.Value, secondFinalPoint.Value);
                                                simulateDrag(firstFinalPoint.Value, secondFinalPoint.Value);
                                            }

                                            ResetAndCleanupAfterMouseAction();
                                        };

                                        if (keyStateService.KeyDownStates[KeyValues.MouseMagnifierKey].Value.IsDownOrLockedDown())
                                        {
                                            ShowCursor = false; //See MouseMoveAndLeftClick case for explanation of this
                                            MagnifiedPointSelectionAction = secondFinalClickAction;
                                            MagnifyAtPoint = repeatFirstClickOrSecondClickPoint;
                                            ShowCursor = true;
                                        }
                                        else
                                        {
                                            secondFinalClickAction(repeatFirstClickOrSecondClickPoint);
                                        }

                                        nextPointSelectionAction = null;
                                    };

                                    if (keyStateService.KeyDownStates[KeyValues.MouseMagnifierKey].Value.IsDownOrLockedDown())
                                    {
                                        nextPointSelectionAction = deferIfMagnifyingElseDoNow;
                                    }
                                    else
                                    {
                                        deferIfMagnifyingElseDoNow(repeatFirstClickOrSecondClickAction);
                                    }
                                };
                            }
                            else
                            {
                                //Reset and clean up if we are not continuing to 2nd point
                                SelectionMode = SelectionModes.Key;
                                nextPointSelectionAction = null;
                                ShowCursor = false;
                                if (keyStateService.KeyDownStates[KeyValues.MouseMagnifierKey].Value == KeyDownStates.Down)
                                {
                                    keyStateService.KeyDownStates[KeyValues.MouseMagnifierKey].Value = KeyDownStates.Up; //Release magnifier if down but not locked down
                                }
                            }

                            //Reset and clean up
                            MagnifyAtPoint = null;
                            MagnifiedPointSelectionAction = null;
                        }, finalClickInSeries: false);
                        break;

                    case FunctionKeys.MouseKeyboard:
                        Log.Debug("Changing keyboard to Mouse.");
                        Action backAction;
                        if (keyStateService.SimulateKeyStrokes
                            && Settings.Default.SuppressModifierKeysWhenInMouseKeyboard)
                        {
                            var lastLeftShiftValue = keyStateService.KeyDownStates[KeyValues.LeftShiftKey].Value;
                            var lastLeftCtrlValue = keyStateService.KeyDownStates[KeyValues.LeftCtrlKey].Value;
                            var lastLeftWinValue = keyStateService.KeyDownStates[KeyValues.LeftWinKey].Value;
                            var lastLeftAltValue = keyStateService.KeyDownStates[KeyValues.LeftAltKey].Value;
                            keyStateService.KeyDownStates[KeyValues.LeftShiftKey].Value = KeyDownStates.Up;
                            keyStateService.KeyDownStates[KeyValues.LeftCtrlKey].Value = KeyDownStates.Up;
                            keyStateService.KeyDownStates[KeyValues.LeftWinKey].Value = KeyDownStates.Up;
                            keyStateService.KeyDownStates[KeyValues.LeftAltKey].Value = KeyDownStates.Up;
                            backAction = () =>
                            {
                                keyStateService.KeyDownStates[KeyValues.LeftShiftKey].Value = lastLeftShiftValue;
                                keyStateService.KeyDownStates[KeyValues.LeftCtrlKey].Value = lastLeftCtrlValue;
                                keyStateService.KeyDownStates[KeyValues.LeftWinKey].Value = lastLeftWinValue;
                                keyStateService.KeyDownStates[KeyValues.LeftAltKey].Value = lastLeftAltValue;
                                Keyboard = currentKeyboard;
                            };
                        }
                        else
                        {
                            backAction = () => Keyboard = currentKeyboard;
                        }
                        Keyboard = new Mouse(backAction);
                        //Reinstate mouse keyboard docked state (if docked)
                        if (Settings.Default.MainWindowState == WindowStates.Docked)
                        {
                            if (Settings.Default.MouseKeyboardDockSize == DockSizes.Full
                                && Settings.Default.MainWindowDockSize != DockSizes.Full)
                            {
                                mainWindowManipulationService.ResizeDockToFull();
                            }
                            else if (Settings.Default.MouseKeyboardDockSize == DockSizes.Collapsed
                                && Settings.Default.MainWindowDockSize != DockSizes.Collapsed)
                            {
                                mainWindowManipulationService.ResizeDockToCollapsed();
                            }
                        }
                        break;

                    case FunctionKeys.MouseLeftClick:
                        var leftClickPoint = mouseOutputService.GetCursorPosition();
                        Log.DebugFormat("Mouse left click selected at point ({0},{1}).", leftClickPoint.X, leftClickPoint.Y);
                        Action<Point?> performLeftClick = point =>
                        {
                            if (point != null)
                            {
                                mouseOutputService.MoveTo(point.Value);
                            }
                            audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                            mouseOutputService.LeftButtonClick();
                        };
                        lastMouseActionStateManager.LastMouseAction = () => performLeftClick(leftClickPoint);
                        performLeftClick(null);
                        break;

                    case FunctionKeys.MouseLeftDoubleClick:
                        var leftDoubleClickPoint = mouseOutputService.GetCursorPosition();
                        Log.DebugFormat("Mouse left double click selected at point ({0},{1}).", leftDoubleClickPoint.X, leftDoubleClickPoint.Y);
                        Action<Point?> performLeftDoubleClick = point =>
                        {
                            if (point != null)
                            {
                                mouseOutputService.MoveTo(point.Value);
                            }
                            audioService.PlaySound(Settings.Default.MouseDoubleClickSoundFile, Settings.Default.MouseDoubleClickSoundVolume);
                            mouseOutputService.LeftButtonDoubleClick();
                        };
                        lastMouseActionStateManager.LastMouseAction = () => performLeftDoubleClick(leftDoubleClickPoint);
                        performLeftDoubleClick(null);
                        break;

                    case FunctionKeys.MouseLeftDownUp:
                        var leftDownUpPoint = mouseOutputService.GetCursorPosition();
                        if (keyStateService.KeyDownStates[KeyValues.MouseLeftDownUpKey].Value.IsDownOrLockedDown())
                        {
                            Log.DebugFormat("Pressing mouse left button down at point ({0},{1}).", leftDownUpPoint.X, leftDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseDownSoundFile, Settings.Default.MouseDownSoundVolume);
                            mouseOutputService.LeftButtonDown();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        else
                        {
                            Log.DebugFormat("Releasing mouse left button at point ({0},{1}).", leftDownUpPoint.X, leftDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseUpSoundFile, Settings.Default.MouseUpSoundVolume);
                            mouseOutputService.LeftButtonUp();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        break;

                    case FunctionKeys.MouseMiddleClick:
                        var middleClickPoint = mouseOutputService.GetCursorPosition();
                        Log.DebugFormat("Mouse middle click selected at point ({0},{1}).", middleClickPoint.X, middleClickPoint.Y);
                        Action<Point?> performMiddleClick = point =>
                        {
                            if (point != null)
                            {
                                mouseOutputService.MoveTo(point.Value);
                            }
                            audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                            mouseOutputService.MiddleButtonClick();
                        };
                        lastMouseActionStateManager.LastMouseAction = () => performMiddleClick(middleClickPoint);
                        performMiddleClick(null);
                        break;

                    case FunctionKeys.MouseMiddleDownUp:
                        var middleDownUpPoint = mouseOutputService.GetCursorPosition();
                        if (keyStateService.KeyDownStates[KeyValues.MouseMiddleDownUpKey].Value.IsDownOrLockedDown())
                        {
                            Log.DebugFormat("Pressing mouse middle button down at point ({0},{1}).", middleDownUpPoint.X, middleDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseDownSoundFile, Settings.Default.MouseDownSoundVolume);
                            mouseOutputService.MiddleButtonDown();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        else
                        {
                            Log.DebugFormat("Releasing mouse middle button at point ({0},{1}).", middleDownUpPoint.X, middleDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseUpSoundFile, Settings.Default.MouseUpSoundVolume);
                            mouseOutputService.MiddleButtonUp();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        break;

                    case FunctionKeys.MouseMoveAndLeftClick:
                        Log.Debug("Mouse move and left click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    Log.DebugFormat("Performing mouse left click at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    mouseOutputService.MoveAndLeftClick(fp, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndLeftDoubleClick:
                        Log.Debug("Mouse move and left double click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    Log.DebugFormat("Performing mouse left double click at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseDoubleClickSoundFile, Settings.Default.MouseDoubleClickSoundVolume);
                                    mouseOutputService.MoveAndLeftDoubleClick(fp, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndMiddleClick:
                        Log.Debug("Mouse move and middle click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    Log.DebugFormat("Performing mouse middle click at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    mouseOutputService.MoveAndMiddleClick(fp, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndRightClick:
                        Log.Debug("Mouse move and right click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    Log.DebugFormat("Performing mouse right click at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    mouseOutputService.MoveAndRightClick(fp, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAmountInPixels:
                        Log.Debug("Progressing MouseMoveAmountInPixels.");
                        switch (Settings.Default.MouseMoveAmountInPixels)
                        {
                            case 1:
                                Settings.Default.MouseMoveAmountInPixels = 5;
                                break;

                            case 5:
                                Settings.Default.MouseMoveAmountInPixels = 10;
                                break;

                            case 10:
                                Settings.Default.MouseMoveAmountInPixels = 25;
                                break;

                            case 25:
                                Settings.Default.MouseMoveAmountInPixels = 50;
                                break;

                            case 50:
                                Settings.Default.MouseMoveAmountInPixels = 100;
                                break;

                            default:
                                Settings.Default.MouseMoveAmountInPixels = 1;
                                break;
                        }
                        break;

                    case FunctionKeys.MouseMoveAndScrollToBottom:
                        Log.Debug("Mouse move and scroll to bottom selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateScrollToBottom = fp =>
                                {
                                    Log.DebugFormat("Performing mouse scroll to bottom at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    mouseOutputService.MoveAndScrollWheelDown(fp, Settings.Default.MouseScrollAmountInClicks, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateScrollToBottom(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateScrollToBottom(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndScrollToLeft:
                        Log.Debug("Mouse move and scroll to left selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateScrollToLeft = fp =>
                                {
                                    Log.DebugFormat("Performing mouse scroll to left at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    mouseOutputService.MoveAndScrollWheelLeft(fp, Settings.Default.MouseScrollAmountInClicks, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateScrollToLeft(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateScrollToLeft(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndScrollToRight:
                        Log.Debug("Mouse move and scroll to right selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateScrollToRight = fp =>
                                {
                                    Log.DebugFormat("Performing mouse scroll to right at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    mouseOutputService.MoveAndScrollWheelRight(fp, Settings.Default.MouseScrollAmountInClicks, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateScrollToRight(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateScrollToRight(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveAndScrollToTop:
                        Log.Debug("Mouse move and scroll to top selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateScrollToTop = fp =>
                                {
                                    Log.DebugFormat("Performing mouse scroll to top at point ({0},{1}).", fp.X, fp.Y);
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    mouseOutputService.MoveAndScrollWheelUp(fp, Settings.Default.MouseScrollAmountInClicks, true);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateScrollToTop(finalPoint.Value);
                                ShowCursor = false; //Hide cursor popup before performing action as it is possible for it to be performed on the popup
                                simulateScrollToTop(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveTo:
                        Log.Debug("Mouse move to selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateMoveTo = fp =>
                                {
                                    Log.DebugFormat("Performing mouse move to point ({0},{1}).", fp.X, fp.Y);
                                    mouseOutputService.MoveTo(fp);
                                };
                                lastMouseActionStateManager.LastMouseAction = () => simulateMoveTo(finalPoint.Value);
                                simulateMoveTo(finalPoint.Value);
                            }
                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMoveToBottom:
                        Log.Debug("Mouse move to bottom selected.");
                        Action simulateMoveToBottom = () =>
                        {
                            var cursorPosition = mouseOutputService.GetCursorPosition();
                            var moveToPoint = new Point(cursorPosition.X, cursorPosition.Y + Settings.Default.MouseMoveAmountInPixels);
                            Log.DebugFormat("Performing mouse move to point ({0},{1}).", moveToPoint.X, moveToPoint.Y);
                            mouseOutputService.MoveTo(moveToPoint);
                        };
                        lastMouseActionStateManager.LastMouseAction = simulateMoveToBottom;
                        simulateMoveToBottom();
                        break;

                    case FunctionKeys.MouseMoveToLeft:
                        Log.Debug("Mouse move to left selected.");
                        Action simulateMoveToLeft = () =>
                        {
                            var cursorPosition = mouseOutputService.GetCursorPosition();
                            var moveToPoint = new Point(cursorPosition.X - Settings.Default.MouseMoveAmountInPixels, cursorPosition.Y);
                            Log.DebugFormat("Performing mouse move to point ({0},{1}).", moveToPoint.X, moveToPoint.Y);
                            mouseOutputService.MoveTo(moveToPoint);
                        };
                        lastMouseActionStateManager.LastMouseAction = simulateMoveToLeft;
                        simulateMoveToLeft();
                        break;

                    case FunctionKeys.MouseMoveToRight:
                        Log.Debug("Mouse move to right selected.");
                        Action simulateMoveToRight = () =>
                        {
                            var cursorPosition = mouseOutputService.GetCursorPosition();
                            var moveToPoint = new Point(cursorPosition.X + Settings.Default.MouseMoveAmountInPixels, cursorPosition.Y);
                            Log.DebugFormat("Performing mouse move to point ({0},{1}).", moveToPoint.X, moveToPoint.Y);
                            mouseOutputService.MoveTo(moveToPoint);
                        };
                        lastMouseActionStateManager.LastMouseAction = simulateMoveToRight;
                        simulateMoveToRight();
                        break;

                    case FunctionKeys.MouseMoveToTop:
                        Log.Debug("Mouse move to top selected.");
                        Action simulateMoveToTop = () =>
                        {
                            var cursorPosition = mouseOutputService.GetCursorPosition();
                            var moveToPoint = new Point(cursorPosition.X, cursorPosition.Y - Settings.Default.MouseMoveAmountInPixels);
                            Log.DebugFormat("Performing mouse move to point ({0},{1}).", moveToPoint.X, moveToPoint.Y);
                            mouseOutputService.MoveTo(moveToPoint);
                        };
                        lastMouseActionStateManager.LastMouseAction = simulateMoveToTop;
                        simulateMoveToTop();
                        break;

                    case FunctionKeys.MouseRightClick:
                        var rightClickPoint = mouseOutputService.GetCursorPosition();
                        Log.DebugFormat("Mouse right click selected at point ({0},{1}).", rightClickPoint.X, rightClickPoint.Y);
                        Action<Point?> performRightClick = point =>
                        {
                            if (point != null)
                            {
                                mouseOutputService.MoveTo(point.Value);
                            }
                            audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                            mouseOutputService.RightButtonClick();
                        };
                        lastMouseActionStateManager.LastMouseAction = () => performRightClick(rightClickPoint);
                        performRightClick(null);
                        break;

                    case FunctionKeys.MouseRightDownUp:
                        var rightDownUpPoint = mouseOutputService.GetCursorPosition();
                        if (keyStateService.KeyDownStates[KeyValues.MouseRightDownUpKey].Value.IsDownOrLockedDown())
                        {
                            Log.DebugFormat("Pressing mouse right button down at point ({0},{1}).", rightDownUpPoint.X, rightDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseDownSoundFile, Settings.Default.MouseDownSoundVolume);
                            mouseOutputService.RightButtonDown();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        else
                        {
                            Log.DebugFormat("Releasing mouse right button at point ({0},{1}).", rightDownUpPoint.X, rightDownUpPoint.Y);
                            audioService.PlaySound(Settings.Default.MouseUpSoundFile, Settings.Default.MouseUpSoundVolume);
                            mouseOutputService.RightButtonUp();
                            lastMouseActionStateManager.LastMouseAction = null;
                        }
                        break;

                    case FunctionKeys.MoveAndResizeAdjustmentAmount:
                        Log.Debug("Progressing MoveAndResizeAdjustmentAmount.");
                        switch (Settings.Default.MoveAndResizeAdjustmentAmountInPixels)
                        {
                            case 1:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 5;
                                break;

                            case 5:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 10;
                                break;

                            case 10:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 25;
                                break;

                            case 25:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 50;
                                break;

                            case 50:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 100;
                                break;

                            default:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 1;
                                break;
                        }
                        break;

                    case FunctionKeys.MouseScrollAmountInClicks:
                        Log.Debug("Progressing MouseScrollAmountInClicks.");
                        switch (Settings.Default.MouseScrollAmountInClicks)
                        {
                            case 1:
                                Settings.Default.MouseScrollAmountInClicks = 3;
                                break;

                            case 3:
                                Settings.Default.MouseScrollAmountInClicks = 5;
                                break;

                            case 5:
                                Settings.Default.MouseScrollAmountInClicks = 10;
                                break;

                            case 10:
                                Settings.Default.MouseScrollAmountInClicks = 25;
                                break;

                            default:
                                Settings.Default.MouseScrollAmountInClicks = 1;
                                break;
                        }
                        break;

                    case FunctionKeys.MoveToBottom:
                        Log.DebugFormat("Moving to bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.Bottom, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndLeft:
                        Log.DebugFormat("Moving to bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.BottomLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndLeftBoundaries:
                        Log.Debug("Moving to bottom and left boundaries.");
                        mainWindowManipulationService.Move(MoveToDirections.BottomLeft, null);
                        break;

                    case FunctionKeys.MoveToBottomAndRight:
                        Log.DebugFormat("Moving to bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.BottomRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndRightBoundaries:
                        Log.Debug("Moving to bottom and right boundaries.");
                        mainWindowManipulationService.Move(MoveToDirections.BottomRight, null);
                        break;

                    case FunctionKeys.MoveToBottomBoundary:
                        Log.Debug("Moving to bottom boundary.");
                        mainWindowManipulationService.Move(MoveToDirections.Bottom, null);
                        break;

                    case FunctionKeys.MoveToLeft:
                        Log.DebugFormat("Moving to left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.Left, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToLeftBoundary:
                        Log.Debug("Moving to left boundary.");
                        mainWindowManipulationService.Move(MoveToDirections.Left, null);
                        break;

                    case FunctionKeys.MoveToRight:
                        Log.DebugFormat("Moving to right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.Right, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToRightBoundary:
                        Log.Debug("Moving to right boundary.");
                        mainWindowManipulationService.Move(MoveToDirections.Right, null);
                        break;

                    case FunctionKeys.MoveToTop:
                        Log.DebugFormat("Moving to top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.Top, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndLeft:
                        Log.DebugFormat("Moving to top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.TopLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndLeftBoundaries:
                        Log.Debug("Moving to top and left boundaries.");
                        mainWindowManipulationService.Move(MoveToDirections.TopLeft, null);
                        break;

                    case FunctionKeys.MoveToTopAndRight:
                        Log.DebugFormat("Moving to top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Move(MoveToDirections.TopRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndRightBoundaries:
                        Log.Debug("Moving to top and right boundaries.");
                        mainWindowManipulationService.Move(MoveToDirections.TopRight, null);
                        break;

                    case FunctionKeys.MoveToTopBoundary:
                        Log.Debug("Moving to top boundary.");
                        mainWindowManipulationService.Move(MoveToDirections.Top, null);
                        break;

                    case FunctionKeys.NextSuggestions:
                        Log.Debug("Incrementing suggestions page.");

                        if (suggestionService.Suggestions != null
                            && (suggestionService.Suggestions.Count > (suggestionService.SuggestionsPage + 1) * SuggestionService.SuggestionsPerPage))
                        {
                            suggestionService.SuggestionsPage++;
                        }
                        break;

                    case FunctionKeys.NoQuestionResult:
                        HandleYesNoQuestionResult(false);
                        break;

                    case FunctionKeys.NumericAndSymbols1Keyboard:
                        Log.Debug("Changing keyboard to NumericAndSymbols1.");
                        Keyboard = new NumericAndSymbols1();
                        break;

                    case FunctionKeys.NumericAndSymbols2Keyboard:
                        Log.Debug("Changing keyboard to NumericAndSymbols2.");
                        Keyboard = new NumericAndSymbols2();
                        break;

                    case FunctionKeys.NumericAndSymbols3Keyboard:
                        Log.Debug("Changing keyboard to Symbols3.");
                        Keyboard = new NumericAndSymbols3();
                        break;

                    case FunctionKeys.PhysicalKeysKeyboard:
                        Log.Debug("Changing keyboard to PhysicalKeys.");
                        Keyboard = new PhysicalKeys();
                        break;

                    case FunctionKeys.PreviousSuggestions:
                        Log.Debug("Decrementing suggestions page.");

                        if (suggestionService.SuggestionsPage > 0)
                        {
                            suggestionService.SuggestionsPage--;
                        }
                        break;

                    case FunctionKeys.Quit:
                        Log.Debug("Quit key selected.");
                        var keyboardBeforeQuit = Keyboard;
                        Keyboard = new YesNoQuestion("Are you sure you would like to quit?",
                            () =>
                            {
                                Keyboard = new YesNoQuestion("Are you absolutely sure that you'd like to quit?",
                                    () => Application.Current.Shutdown(),
                                    () => { Keyboard = keyboardBeforeQuit; });
                            },
                            () => { Keyboard = keyboardBeforeQuit; });
                        break;

                    case FunctionKeys.RepeatLastMouseAction:
                        if (lastMouseActionStateManager.LastMouseAction != null)
                        {
                            lastMouseActionStateManager.LastMouseAction();
                        }
                        break;

                    case FunctionKeys.ShrinkFromBottom:
                        Log.DebugFormat("Shrinking from bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.Bottom, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromBottomAndLeft:
                        Log.DebugFormat("Shrinking from bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.BottomLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromBottomAndRight:
                        Log.DebugFormat("Shrinking from bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.BottomRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromLeft:
                        Log.DebugFormat("Shrinking from left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.Left, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromRight:
                        Log.DebugFormat("Shrinking from right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.Right, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTop:
                        Log.DebugFormat("Shrinking from top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.Top, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTopAndLeft:
                        Log.DebugFormat("Shrinking from top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.TopLeft, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTopAndRight:
                        Log.DebugFormat("Shrinking from top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.Shrink(ShrinkFromDirections.TopRight, Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.SizeAndPositionKeyboard:
                        Log.Debug("Changing keyboard to Size & Position.");
                        Keyboard = new SizeAndPosition(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.Speak:
                        var speechStarted = audioService.SpeakNewOrInterruptCurrentSpeech(
                            keyboardOutputService.Text,
                            () => { KeyStateService.KeyDownStates[KeyValues.SpeakKey].Value = KeyDownStates.Up; },
                            Settings.Default.SpeechVolume,
                            Settings.Default.SpeechRate,
                            Settings.Default.SpeechVoice);
                        KeyStateService.KeyDownStates[KeyValues.SpeakKey].Value = speechStarted ? KeyDownStates.Down : KeyDownStates.Up;
                        break;

                    case FunctionKeys.YesQuestionResult:
                        HandleYesNoQuestionResult(true);
                        break;
                }

                keyboardOutputService.ProcessFunctionKey(singleKeyValue.FunctionKey.Value);
            }
        }
예제 #2
0
        private void InitialiseKeyboard(IWindowManipulationService windowManipulationService)
        {
            if (Settings.Default.ConversationOnlyMode)
            {
                Keyboard = new ConversationAlpha(null);
                windowManipulationService.Maximise();
            }
            else
            {
                switch (Settings.Default.StartupKeyboard)
                {
                    case Enums.Keyboards.Alpha:
                        Keyboard = new Alpha();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.ConversationAlpha:
                        Keyboard = new ConversationAlpha(() =>
                        {
                            Keyboard = new Menu(() => Keyboard = new Alpha());
                            mainWindowManipulationService.Restore();
                            mainWindowManipulationService.ResizeDockToFull();
                        });
                        windowManipulationService.Maximise();
                        break;

                    case Enums.Keyboards.ConversationNumericAndSymbols:
                        Keyboard = new ConversationNumericAndSymbols(() =>
                        {
                            Keyboard = new Menu(() => Keyboard = new Alpha());
                            mainWindowManipulationService.Restore();
                            mainWindowManipulationService.ResizeDockToFull();
                        });
                        windowManipulationService.Maximise();
                        break;

                    case Enums.Keyboards.Currencies1:
                        Keyboard = new Currencies1();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Currencies2:
                        Keyboard = new Currencies2();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Diacritics1:
                        Keyboard = new Diacritics1();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Diacritics2:
                        Keyboard = new Diacritics2();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Diacritics3:
                        Keyboard = new Diacritics3();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Menu:
                        Keyboard = new Menu(() => Keyboard = new Alpha());
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.Minimised:
                        Keyboard = new Minimised(() =>
                        {
                            Keyboard = new Menu(() => Keyboard = new Alpha());
                            windowManipulationService.Restore();
                            mainWindowManipulationService.ResizeDockToFull();
                        });
                        windowManipulationService.Minimise();
                        break;

                    case Enums.Keyboards.Mouse:
                        Keyboard = new Mouse(() => Keyboard = new Menu(() => Keyboard = new Alpha()));
                        windowManipulationService.Restore();
                        if (Settings.Default.MouseKeyboardDockSize == DockSizes.Full)
                        {
                            mainWindowManipulationService.ResizeDockToFull();
                        }
                        else
                        {
                            mainWindowManipulationService.ResizeDockToCollapsed();
                        }
                        break;

                    case Enums.Keyboards.NumericAndSymbols1:
                        Keyboard = new NumericAndSymbols1();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.NumericAndSymbols2:
                        Keyboard = new NumericAndSymbols2();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.NumericAndSymbols3:
                        Keyboard = new NumericAndSymbols3();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.PhysicalKeys:
                        Keyboard = new PhysicalKeys();
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;

                    case Enums.Keyboards.SizeAndPosition:
                        Keyboard = new SizeAndPosition(() => Keyboard = new Menu(() => Keyboard = new Alpha()));
                        windowManipulationService.Restore();
                        mainWindowManipulationService.ResizeDockToFull();
                        break;
                }
            }
        }
        private void HandleFunctionKeySelectionResult(KeyValue singleKeyValue)
        {
            if (singleKeyValue.FunctionKey != null)
            {
                keyboardService.ProgressKeyDownState(singleKeyValue);

                var currentKeyboard = Keyboard;

                switch (singleKeyValue.FunctionKey.Value)
                {
                    case FunctionKeys.AddToDictionary:
                        AddTextToDictionary();
                        break;

                    case FunctionKeys.AlphaKeyboard:
                        Log.Debug("Changing keyboard to Alpha.");
                        Keyboard = new Alpha();
                        break;

                    case FunctionKeys.ArrangeWindowsHorizontally:
                        Log.Debug("Arranging windows horizontally.");
                        mainWindowManipulationService.ArrangeWindowsHorizontally();
                        break;

                    case FunctionKeys.ArrangeWindowsMaximised:
                        Log.Debug("Arranging windows maximised.");
                        mainWindowManipulationService.ArrangeWindowsMaximised();
                        break;

                    case FunctionKeys.ArrangeWindowsVertically:
                        Log.Debug("Arranging windows vertically.");
                        mainWindowManipulationService.ArrangeWindowsVertically();
                        break;

                    case FunctionKeys.Diacritic1Keyboard:
                        Log.Debug("Changing keyboard to Diacritic1.");
                        Keyboard = new Diacritic1();
                        break;

                    case FunctionKeys.Diacritic2Keyboard:
                        Log.Debug("Changing keyboard to Diacritic2.");
                        Keyboard = new Diacritic2();
                        break;

                    case FunctionKeys.Diacritic3Keyboard:
                        Log.Debug("Changing keyboard to Diacritic3.");
                        Keyboard = new Diacritic3();
                        break;

                    case FunctionKeys.BackFromKeyboard:
                        Log.Debug("Navigating back from keyboard.");
                        var navigableKeyboard = Keyboard as IBackAction;
                        if (navigableKeyboard != null && navigableKeyboard.BackAction != null)
                        {
                            navigableKeyboard.BackAction();
                        }
                        else
                        {
                            Keyboard = new Alpha();
                        }
                        break;

                    case FunctionKeys.Calibrate:
                        if (CalibrationService != null)
                        {
                            Log.Debug("Calibrate requested.");

                            var previousKeyboard = Keyboard;

                            var question = CalibrationService.CanBeCompletedWithoutManualIntervention
                            ? "Are you sure you would like to re-calibrate?"
                            : "Calibration cannot be completed without manual intervention, e.g. having to use a mouse. You may be stuck in the calibration process if you cannot manually interact with your computer.\nAre you sure you would like to re-calibrate?";

                            Keyboard = new YesNoQuestion(
                                question,
                                () =>
                                {
                                    inputService.RequestSuspend();
                                    Keyboard = previousKeyboard;
                                    CalibrateRequest.Raise(new NotificationWithCalibrationResult(), calibrationResult =>
                                    {
                                        if (calibrationResult.Success)
                                        {
                                            audioService.PlaySound(Settings.Default.InfoSoundFile, Settings.Default.InfoSoundVolume);
                                            RaiseToastNotification("Success", calibrationResult.Message, NotificationTypes.Normal, () => { inputService.RequestResume(); });
                                        }
                                        else
                                        {
                                            audioService.PlaySound(Settings.Default.ErrorSoundFile, Settings.Default.ErrorSoundVolume);
                                            RaiseToastNotification("Uh-oh!", calibrationResult.Exception != null
                                                    ? calibrationResult.Exception.Message
                                                    : calibrationResult.Message ?? "Something went wrong, but I don't know what - please check the logs",
                                                NotificationTypes.Error,
                                                () => { inputService.RequestResume(); });
                                        }
                                    });
                                },
                                () =>
                                {
                                    Keyboard = previousKeyboard;
                                });
                        }
                        break;

                    case FunctionKeys.Currencies1Keyboard:
                        Log.Debug("Changing keyboard to Currencies1.");
                        Keyboard = new Currencies1();
                        break;

                    case FunctionKeys.Currencies2Keyboard:
                        Log.Debug("Changing keyboard to Currencies2.");
                        Keyboard = new Currencies2();
                        break;

                    case FunctionKeys.DecreaseOpacity:
                        Log.Debug("Decreasing opacity.");
                        mainWindowManipulationService.DecreaseOpacity();
                        break;

                    case FunctionKeys.ExpandToBottom:
                        Log.DebugFormat("Expanding to bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToBottom(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToBottomAndLeft:
                        Log.DebugFormat("Expanding to bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToBottomAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToBottomAndRight:
                        Log.DebugFormat("Expanding to bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToBottomAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToLeft:
                        Log.DebugFormat("Expanding to left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToRight:
                        Log.DebugFormat("Expanding to right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTop:
                        Log.DebugFormat("Expanding to top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToTop(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTopAndLeft:
                        Log.DebugFormat("Expanding to top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToTopAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ExpandToTopAndRight:
                        Log.DebugFormat("Expanding to top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ExpandToTopAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.IncreaseOpacity:
                        Log.Debug("Increasing opacity.");
                        mainWindowManipulationService.IncreaseOpacity();
                        break;

                    case FunctionKeys.MaximiseSize:
                        Log.Debug("Maximising size.");
                        mainWindowManipulationService.Maximise();
                        break;

                    case FunctionKeys.MenuKeyboard:
                        Log.Debug("Changing keyboard to Menu.");
                        Keyboard = new Menu(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToBottomAndLeftBoundaries:
                        Log.Debug("Minimising to bottom and left boundaries.");
                        mainWindowManipulationService.MinimiseToBottomAndLeftBoundaries();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToBottomAndRightBoundaries:
                        Log.Debug("Minimising to bottom and right boundaries.");
                        mainWindowManipulationService.MinimiseToBottomAndRightBoundaries();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToMiddleOfBottomBoundary:
                        Log.Debug("Minimising to bottom boundary.");
                        mainWindowManipulationService.MinimiseToMiddleOfBottomBoundary();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToMiddleOfLeftBoundary:
                        Log.Debug("Minimising to left boundary.");
                        mainWindowManipulationService.MinimiseToMiddleOfLeftBoundary();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToMiddleOfRightBoundary:
                        Log.Debug("Minimising to right boundary.");
                        mainWindowManipulationService.MinimiseToMiddleOfRightBoundary();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToTopAndLeftBoundaries:
                        Log.Debug("Minimising to top and left boundaries.");
                        mainWindowManipulationService.MinimiseToTopAndLeftBoundaries();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToTopAndRightBoundaries:
                        Log.Debug("Minimising to top and right boundaries.");
                        mainWindowManipulationService.MinimiseToTopAndRightBoundaries();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MinimiseToMiddleOfTopBoundary:
                        Log.Debug("Minimising to top boundary.");
                        mainWindowManipulationService.MinimiseToMiddleOfTopBoundary();
                        Log.Debug("Changing keyboard to Minimised.");
                        Keyboard = new Minimised(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.MouseKeyboard:
                        Log.Debug("Changing keyboard to Mouse.");
                        if (keyboardService.KeyDownStates[KeyValues.SimulateKeyStrokesKey].Value.IsDownOrLockedDown()
                            && Settings.Default.DisableKeyStrokeSimulationWhileMouseKeyboardIsOpen)
                        {
                            var lastSimulateKeyStrokesValue = keyboardService.KeyDownStates[KeyValues.SimulateKeyStrokesKey].Value;
                            var lastLeftShiftValue = keyboardService.KeyDownStates[KeyValues.LeftShiftKey].Value;
                            var lastLeftCtrlValue = keyboardService.KeyDownStates[KeyValues.LeftCtrlKey].Value;
                            var lastLeftWinValue = keyboardService.KeyDownStates[KeyValues.LeftWinKey].Value;
                            var lastLeftAltValue = keyboardService.KeyDownStates[KeyValues.LeftAltKey].Value;
                            keyboardService.KeyDownStates[KeyValues.SimulateKeyStrokesKey].Value = KeyDownStates.Up;
                            Keyboard = new Mouse(() =>
                            {
                                keyboardService.KeyDownStates[KeyValues.SimulateKeyStrokesKey].Value = lastSimulateKeyStrokesValue;
                                keyboardService.KeyDownStates[KeyValues.LeftShiftKey].Value = lastLeftShiftValue;
                                keyboardService.KeyDownStates[KeyValues.LeftCtrlKey].Value = lastLeftCtrlValue;
                                keyboardService.KeyDownStates[KeyValues.LeftWinKey].Value = lastLeftWinValue;
                                keyboardService.KeyDownStates[KeyValues.LeftAltKey].Value = lastLeftAltValue;
                                Keyboard = currentKeyboard;
                            });
                        }
                        else
                        {
                            Keyboard = new Mouse(() => Keyboard = currentKeyboard);
                        }
                        break;

                    case FunctionKeys.MouseDoubleLeftClick:
                        Log.Debug("Mouse double left click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseDoubleClickSoundFile, Settings.Default.MouseDoubleClickSoundVolume);
                                    outputService.LeftMouseButtonDoubleClick(fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseDrag:
                        Log.Debug("Mouse drag selected.");
                        SetupFinalClickAction(firstFinalPoint =>
                        {
                            if (firstFinalPoint != null)
                            {
                                //This class reacts to the point selection event AFTER the MagnifyPopup reacts to it.
                                //This means that if the MagnifyPopup sets the nextPointSelectionAction from the
                                //MagnifiedPointSelectionAction then it will be called immediately i.e. for the same point.
                                //The workaround is to set the nextPointSelectionAction to a lambda which sets the NEXT
                                //nextPointSelectionAction. This means the immediate call to the lambda just sets up the
                                //delegate for the subsequent call.
                                nextPointSelectionAction = repeatFirstClickOrSecondClickAction =>
                                {
                                    Action<Point> deferIfMagnifyingElseDoNow = repeatFirstClickOrSecondClickPoint =>
                                    {
                                        Action<Point?> secondFinalClickAction = secondFinalPoint =>
                                        {
                                            if (secondFinalPoint != null)
                                            {
                                                Action<Point, Point> simulateDrag = (fp1, fp2) =>
                                                {
                                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                                    outputService.LeftMouseButtonDown(fp1);
                                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                                    outputService.LeftMouseButtonUp(fp2);
                                                };

                                                lastMouseActionStateManager.LastMouseAction =
                                                    () => simulateDrag(firstFinalPoint.Value, secondFinalPoint.Value);
                                                simulateDrag(firstFinalPoint.Value, secondFinalPoint.Value);
                                            }

                                            ResetAndCleanupAfterMouseAction();
                                        };

                                        if (keyboardService.KeyDownStates[KeyValues.MouseMagnifierKey].Value.IsDownOrLockedDown())
                                        {
                                            ShowCursor = false; //See MouseLeftClick case for explanation of this
                                            MagnifiedPointSelectionAction = secondFinalClickAction;
                                            MagnifyAtPoint = repeatFirstClickOrSecondClickPoint;
                                            ShowCursor = true;
                                        }
                                        else
                                        {
                                            secondFinalClickAction(repeatFirstClickOrSecondClickPoint);
                                        }

                                        nextPointSelectionAction = null;
                                    };

                                    if (keyboardService.KeyDownStates[KeyValues.MouseMagnifierKey].Value.IsDownOrLockedDown())
                                    {
                                        nextPointSelectionAction = deferIfMagnifyingElseDoNow;
                                    }
                                    else
                                    {
                                        deferIfMagnifyingElseDoNow(repeatFirstClickOrSecondClickAction);
                                    }
                                };
                            }
                            else
                            {
                                //Reset and clean up if we are not continuing to 2nd point
                                SelectionMode = SelectionModes.Key;
                                nextPointSelectionAction = null;
                                ShowCursor = false;
                                if (keyboardService.KeyDownStates[KeyValues.MouseMagnifierKey].Value == KeyDownStates.Down)
                                {
                                    keyboardService.KeyDownStates[KeyValues.MouseMagnifierKey].Value = KeyDownStates.Up; //Release magnifier if down but not locked down
                                }
                            }

                            //Reset and clean up
                            MagnifyAtPoint = null;
                            MagnifiedPointSelectionAction = null;
                        }, finalClickInSeries: false);
                        break;

                    case FunctionKeys.MouseLeftClick:
                        Log.Debug("Mouse left click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    outputService.LeftMouseButtonClick(fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseMiddleClick:
                        Log.Debug("Mouse middle click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    outputService.MiddleMouseButtonClick(fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseRightClick:
                        Log.Debug("Mouse right click selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseClickSoundFile, Settings.Default.MouseClickSoundVolume);
                                    outputService.RightMouseButtonClick(fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollAmountInClicks:
                        Log.Debug("Progressing MouseScrollAmountInClicks.");
                        switch (Settings.Default.MouseScrollAmountInClicks)
                        {
                            case 1:
                                Settings.Default.MouseScrollAmountInClicks = 3;
                                break;

                            case 3:
                                Settings.Default.MouseScrollAmountInClicks = 5;
                                break;

                            case 5:
                                Settings.Default.MouseScrollAmountInClicks = 10;
                                break;

                            case 10:
                                Settings.Default.MouseScrollAmountInClicks = 25;
                                break;

                            default:
                                Settings.Default.MouseScrollAmountInClicks = 1;
                                break;
                        }
                        break;

                    case FunctionKeys.MouseScrollToBottom:
                        Log.Debug("Mouse scroll to bottom selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelDown(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToBottomAndLeft:
                        Log.Debug("Mouse scroll to bottom and left selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelDownAndLeft(
                                        Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToBottomAndRight:
                        Log.Debug("Mouse scroll to bottom and right selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelDownAndRight(
                                        Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToLeft:
                        Log.Debug("Mouse scroll to left selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelLeft(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToRight:
                        Log.Debug("Mouse scroll to right selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelRight(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToTop:
                        Log.Debug("Mouse scroll to top selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelUp(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToTopAndLeft:
                        Log.Debug("Mouse scroll to top and left selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelUpAndLeft(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MouseScrollToTopAndRight:
                        Log.Debug("Mouse scroll to top and right selected.");
                        SetupFinalClickAction(finalPoint =>
                        {
                            if (finalPoint != null)
                            {
                                Action<Point> simulateClick = fp =>
                                {
                                    audioService.PlaySound(Settings.Default.MouseScrollSoundFile, Settings.Default.MouseScrollSoundVolume);
                                    outputService.ScrollMouseWheelUpAndRight(Settings.Default.MouseScrollAmountInClicks, fp);
                                };

                                lastMouseActionStateManager.LastMouseAction = () => simulateClick(finalPoint.Value);
                                simulateClick(finalPoint.Value);
                            }

                            //Reset and clean up
                            ResetAndCleanupAfterMouseAction();
                        });
                        break;

                    case FunctionKeys.MoveAndResizeAdjustmentAmount:
                        Log.Debug("Progressing MoveAndResizeAdjustmentAmount.");
                        switch (Settings.Default.MoveAndResizeAdjustmentAmountInPixels)
                        {
                            case 1:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 5;
                                break;

                            case 5:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 10;
                                break;

                            case 10:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 25;
                                break;

                            case 25:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 50;
                                break;

                            case 50:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 100;
                                break;

                            default:
                                Settings.Default.MoveAndResizeAdjustmentAmountInPixels = 1;
                                break;
                        }
                        break;

                    case FunctionKeys.MoveToBottom:
                        Log.DebugFormat("Moving to bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToBottom(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndLeft:
                        Log.DebugFormat("Moving to bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToBottomAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndLeftBoundaries:
                        Log.Debug("Moving to bottom and left boundaries.");
                        mainWindowManipulationService.MoveToBottomAndLeftBoundaries();
                        break;

                    case FunctionKeys.MoveToBottomAndRight:
                        Log.DebugFormat("Moving to bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToBottomAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToBottomAndRightBoundaries:
                        Log.Debug("Moving to bottom and right boundaries.");
                        mainWindowManipulationService.MoveToBottomAndRightBoundaries();
                        break;

                    case FunctionKeys.MoveToBottomBoundary:
                        Log.Debug("Moving to bottom boundary.");
                        mainWindowManipulationService.MoveToBottomBoundary();
                        break;

                    case FunctionKeys.MoveToLeft:
                        Log.DebugFormat("Moving to left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToLeftBoundary:
                        Log.Debug("Moving to left boundary.");
                        mainWindowManipulationService.MoveToLeftBoundary();
                        break;

                    case FunctionKeys.MoveToRight:
                        Log.DebugFormat("Moving to right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToRightBoundary:
                        Log.Debug("Moving to right boundary.");
                        mainWindowManipulationService.MoveToRightBoundary();
                        break;

                    case FunctionKeys.MoveToTop:
                        Log.DebugFormat("Moving to top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToTop(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndLeft:
                        Log.DebugFormat("Moving to top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToTopAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndLeftBoundaries:
                        Log.Debug("Moving to top and left boundaries.");
                        mainWindowManipulationService.MoveToTopAndLeftBoundaries();
                        break;

                    case FunctionKeys.MoveToTopAndRight:
                        Log.DebugFormat("Moving to top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.MoveToTopAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.MoveToTopAndRightBoundaries:
                        Log.Debug("Moving to top and right boundaries.");
                        mainWindowManipulationService.MoveToTopAndRightBoundaries();
                        break;

                    case FunctionKeys.MoveToTopBoundary:
                        Log.Debug("Moving to top boundary.");
                        mainWindowManipulationService.MoveToTopBoundary();
                        break;

                    case FunctionKeys.NextSuggestions:
                        Log.Debug("Incrementing suggestions page.");

                        if (suggestionService.Suggestions != null
                            && (suggestionService.Suggestions.Count > (suggestionService.SuggestionsPage + 1) * SuggestionService.SuggestionsPerPage))
                        {
                            suggestionService.SuggestionsPage++;
                        }
                        break;

                    case FunctionKeys.NoQuestionResult:
                        HandleYesNoQuestionResult(false);
                        break;

                    case FunctionKeys.NumericAndSymbols1Keyboard:
                        Log.Debug("Changing keyboard to NumericAndSymbols1.");
                        Keyboard = new NumericAndSymbols1();
                        break;

                    case FunctionKeys.NumericAndSymbols2Keyboard:
                        Log.Debug("Changing keyboard to NumericAndSymbols2.");
                        Keyboard = new NumericAndSymbols2();
                        break;

                    case FunctionKeys.NumericAndSymbols3Keyboard:
                        Log.Debug("Changing keyboard to Symbols3.");
                        Keyboard = new NumericAndSymbols3();
                        break;

                    case FunctionKeys.PhysicalKeysKeyboard:
                        Log.Debug("Changing keyboard to PhysicalKeys.");
                        Keyboard = new PhysicalKeys();
                        break;

                    case FunctionKeys.PositionKeyboard:
                        Log.Debug("Changing keyboard to Position.");
                        Keyboard = new Position(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.PreviousSuggestions:
                        Log.Debug("Decrementing suggestions page.");

                        if (suggestionService.SuggestionsPage > 0)
                        {
                            suggestionService.SuggestionsPage--;
                        }
                        break;

                    case FunctionKeys.RepeatLastMouseAction:
                        if (lastMouseActionStateManager.LastMouseAction != null)
                        {
                            lastMouseActionStateManager.LastMouseAction();
                        }
                        break;

                    case FunctionKeys.RestoreFromMaximised:
                        Log.Debug("Restoring from maximised.");
                        mainWindowManipulationService.RestoreFromMaximised();
                        break;

                    case FunctionKeys.RestoreFromMinimised:
                        Log.Debug("Restoring from minimised.");
                        var minimisedAsNavigableKeyboard = Keyboard as IBackAction;
                        if (minimisedAsNavigableKeyboard != null && minimisedAsNavigableKeyboard.BackAction != null)
                        {
                            minimisedAsNavigableKeyboard.BackAction();
                        }
                        else
                        {
                            Keyboard = new Alpha();
                        }
                        mainWindowManipulationService.RestoreFromMinimised();
                        break;

                    case FunctionKeys.ShrinkFromBottom:
                        Log.DebugFormat("Shrinking from bottom by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromBottom(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromBottomAndLeft:
                        Log.DebugFormat("Shrinking from bottom and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromBottomAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromBottomAndRight:
                        Log.DebugFormat("Shrinking from bottom and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromBottomAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromLeft:
                        Log.DebugFormat("Shrinking from left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromRight:
                        Log.DebugFormat("Shrinking from right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTop:
                        Log.DebugFormat("Shrinking from top by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromTop(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTopAndLeft:
                        Log.DebugFormat("Shrinking from top and left by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromTopAndLeft(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.ShrinkFromTopAndRight:
                        Log.DebugFormat("Shrinking from top and right by {0}px.", Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        mainWindowManipulationService.ShrinkFromTopAndRight(Settings.Default.MoveAndResizeAdjustmentAmountInPixels);
                        break;

                    case FunctionKeys.SizeKeyboard:
                        Log.Debug("Changing keyboard to Size.");
                        Keyboard = new Size(() => Keyboard = currentKeyboard);
                        break;

                    case FunctionKeys.Speak:
                        audioService.Speak(
                            outputService.Text,
                            Settings.Default.SpeechVolume,
                            Settings.Default.SpeechRate,
                            Settings.Default.SpeechVoice);
                        break;

                    case FunctionKeys.YesQuestionResult:
                        HandleYesNoQuestionResult(true);
                        break;
                }

                outputService.ProcessFunctionKey(singleKeyValue.FunctionKey.Value);
            }
        }