From da7e65e7e82a6923235be85f1be1f43abe070ea2 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Wed, 13 Sep 2017 08:33:12 +0200 Subject: [PATCH] SEBWIN-110: Implemented a basic version of the keyboard layout system control. --- SafeExamBrowser.Configuration/Settings.cs | 4 +- .../Configuration/Settings/ISettings.cs | 5 + SafeExamBrowser.Contracts/I18n/TextKey.cs | 2 + .../SafeExamBrowser.Contracts.csproj | 2 + .../SystemComponents/IKeyboardLayout.cs | 35 +++++++ .../UserInterface/IUserInterfaceFactory.cs | 5 + .../Taskbar/ISystemKeyboardLayoutControl.cs | 27 ++++++ .../Behaviour/Operations/TaskbarOperation.cs | 50 +++++++--- SafeExamBrowser.Core/I18n/Text.xml | 70 ++++++------- .../KeyboardLayout.cs | 90 +++++++++++++++++ .../KeyboardLayoutDefinition.cs | 29 ++++++ .../PowerSupply.cs | 10 +- .../SafeExamBrowser.SystemComponents.csproj | 2 + .../Controls/ApplicationButton.xaml.cs | 2 +- .../Controls/KeyboardLayoutButton.xaml | 30 ++++++ .../Controls/KeyboardLayoutButton.xaml.cs | 40 ++++++++ .../Controls/KeyboardLayoutControl.xaml | 40 ++++++++ .../Controls/KeyboardLayoutControl.xaml.cs | 97 +++++++++++++++++++ .../Images/Reload.xaml | 2 +- ...feExamBrowser.UserInterface.Classic.csproj | 14 +++ .../UserInterfaceFactory.cs | 5 + .../UserInterfaceFactory.cs | 6 ++ SafeExamBrowser/CompositionRoot.cs | 4 +- 23 files changed, 514 insertions(+), 57 deletions(-) create mode 100644 SafeExamBrowser.Contracts/SystemComponents/IKeyboardLayout.cs create mode 100644 SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemKeyboardLayoutControl.cs create mode 100644 SafeExamBrowser.SystemComponents/KeyboardLayout.cs create mode 100644 SafeExamBrowser.SystemComponents/KeyboardLayoutDefinition.cs create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml.cs create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml.cs diff --git a/SafeExamBrowser.Configuration/Settings.cs b/SafeExamBrowser.Configuration/Settings.cs index fdcb7c0e..b5cfedc6 100644 --- a/SafeExamBrowser.Configuration/Settings.cs +++ b/SafeExamBrowser.Configuration/Settings.cs @@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration } public bool AllowApplicationLog => true; - + public bool AllowKeyboardLayout => true; public string AppDataFolderName => "SafeExamBrowser"; public string ApplicationLogFile @@ -37,9 +37,7 @@ namespace SafeExamBrowser.Configuration } public IBrowserSettings Browser { get; private set; } - public IKeyboardSettings Keyboard { get; private set; } - public IMouseSettings Mouse { get; private set; } public string LogFolderPath diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs index c777ec57..c2fb15a3 100644 --- a/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs +++ b/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs @@ -15,6 +15,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings /// bool AllowApplicationLog { get; } + /// + /// Determines whether the user may switch the keyboard layout during runtime. + /// + bool AllowKeyboardLayout { get; } + /// /// The name used for the application data folder. /// diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index ab03bb45..2b84a3fd 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -41,6 +41,7 @@ namespace SafeExamBrowser.Contracts.I18n SplashScreen_StopProcessMonitoring, SplashScreen_StopWindowMonitoring, SplashScreen_TerminateBrowser, + SplashScreen_TerminateTaskbar, SplashScreen_WaitExplorerStartup, SplashScreen_WaitExplorerTermination, SystemControl_BatteryCharged, @@ -48,6 +49,7 @@ namespace SafeExamBrowser.Contracts.I18n SystemControl_BatteryChargeCriticalWarning, SystemControl_BatteryChargeLowInfo, SystemControl_BatteryRemainingCharge, + SystemControl_KeyboardLayoutTooltip, Version } } diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 06f174d7..c5142fc6 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -87,12 +87,14 @@ + + diff --git a/SafeExamBrowser.Contracts/SystemComponents/IKeyboardLayout.cs b/SafeExamBrowser.Contracts/SystemComponents/IKeyboardLayout.cs new file mode 100644 index 00000000..36efdf56 --- /dev/null +++ b/SafeExamBrowser.Contracts/SystemComponents/IKeyboardLayout.cs @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; + +namespace SafeExamBrowser.Contracts.SystemComponents +{ + public interface IKeyboardLayout + { + /// + /// The three-letter ISO code of the culture to which this keyboard layout is associated. + /// + string CultureCode { get; } + + /// + /// The unique identifier of the keyboard layout. + /// + Guid Id { get; } + + /// + /// Specifies whether this is the current keyboard layout. + /// + bool IsCurrent { get; } + + /// + /// The name of this keyboard layout. + /// + string Name { get; } + } +} diff --git a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs index 5a483300..531405f2 100644 --- a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs @@ -41,6 +41,11 @@ namespace SafeExamBrowser.Contracts.UserInterface /// INotificationButton CreateNotification(INotificationInfo info); + /// + /// Creates a system control which allows to change the keyboard layout of the computer. + /// + ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(); + /// /// Creates a system control displaying the power supply status of the computer. /// diff --git a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemKeyboardLayoutControl.cs b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemKeyboardLayoutControl.cs new file mode 100644 index 00000000..0bcc9f39 --- /dev/null +++ b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemKeyboardLayoutControl.cs @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Contracts.SystemComponents; + +namespace SafeExamBrowser.Contracts.UserInterface.Taskbar +{ + public delegate void KeyboardLayoutSelectedEventHandler(IKeyboardLayout layout); + + public interface ISystemKeyboardLayoutControl : ISystemControl + { + /// + /// Event fired when the user selected a keyboard layout. + /// + event KeyboardLayoutSelectedEventHandler LayoutSelected; + + /// + /// Adds the given layout to the list of selectable keyboard layouts. + /// + void Add(IKeyboardLayout layout); + } +} diff --git a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs index f24e4a73..189c75bf 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs @@ -21,8 +21,9 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public class TaskbarOperation : IOperation { private ILogger logger; - private INotificationController aboutController, logController; + private INotificationController logController; private ISettings settings; + private ISystemComponent keyboardLayout; private ISystemComponent powerSupply; private ISystemInfo systemInfo; private ITaskbar taskbar; @@ -34,6 +35,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public TaskbarOperation( ILogger logger, ISettings settings, + ISystemComponent keyboardLayout, ISystemComponent powerSupply, ISystemInfo systemInfo, ITaskbar taskbar, @@ -42,6 +44,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations { this.logger = logger; this.settings = settings; + this.keyboardLayout = keyboardLayout; this.powerSupply = powerSupply; this.systemInfo = systemInfo; this.taskbar = taskbar; @@ -59,16 +62,31 @@ namespace SafeExamBrowser.Core.Behaviour.Operations CreateLogNotification(); } + if (settings.AllowKeyboardLayout) + { + AddKeyboardLayoutControl(); + } + if (systemInfo.HasBattery) { - CreatePowerSupplyComponent(); + AddPowerSupplyControl(); } } public void Revert() { - logController?.Terminate(); - aboutController?.Terminate(); + logger.Info("Terminating taskbar..."); + SplashScreen.UpdateText(TextKey.SplashScreen_TerminateTaskbar); + + if (settings.AllowApplicationLog) + { + logController?.Terminate(); + } + + if (settings.AllowKeyboardLayout) + { + keyboardLayout.Terminate(); + } if (systemInfo.HasBattery) { @@ -76,6 +94,22 @@ namespace SafeExamBrowser.Core.Behaviour.Operations } } + private void AddKeyboardLayoutControl() + { + var control = uiFactory.CreateKeyboardLayoutControl(); + + keyboardLayout.Initialize(control); + taskbar.AddSystemControl(control); + } + + private void AddPowerSupplyControl() + { + var control = uiFactory.CreatePowerSupplyControl(); + + powerSupply.Initialize(control); + taskbar.AddSystemControl(control); + } + private void CreateLogNotification() { var logInfo = new LogNotificationInfo(text); @@ -86,13 +120,5 @@ namespace SafeExamBrowser.Core.Behaviour.Operations taskbar.AddNotification(logNotification); } - - private void CreatePowerSupplyComponent() - { - var control = uiFactory.CreatePowerSupplyControl(); - - powerSupply.Initialize(control); - taskbar.AddSystemControl(control); - } } } diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index 58850c3f..cdb789b7 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -1,37 +1,39 @@  - Open Console - Application Log - An unexpected error occurred during the shutdown procedure! Please consult the application log for more information... - Shutdown Error - An unexpected error occurred during the startup procedure! Please consult the application log for more information... - Startup Error - About Safe Exam Browser - Application Log - Emptying clipboard - Initializing browser - Initializing process monitoring - Initializing taskbar - Initializing window monitoring - Initializing working area - Restoring working area - Initiating shutdown procedure - Starting event handling - Starting keyboard interception - Starting mouse interception - Initiating startup procedure - Stopping event handling - Stopping keyboard interception - Stopping mouse interception - Stopping process monitoring - Stopping window monitoring - Terminating browser - Waiting for Windows explorer to start up - Waiting for Windows explorer to shut down - Plugged in, charging... (%%CHARGE%%%) - Fully charged (%%CHARGE%%%) - The battery charge is critically low. Please connect your computer to a power supply! - The battery charge is getting low. Consider connecting your computer to a power supply in time... - %%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%) - Version + Open Console + Application Log + An unexpected error occurred during the shutdown procedure! Please consult the application log for more information... + Shutdown Error + An unexpected error occurred during the startup procedure! Please consult the application log for more information... + Startup Error + About Safe Exam Browser + Application Log + Emptying clipboard + Initializing browser + Initializing process monitoring + Initializing taskbar + Initializing window monitoring + Initializing working area + Restoring working area + Initiating shutdown procedure + Starting event handling + Starting keyboard interception + Starting mouse interception + Initiating startup procedure + Stopping event handling + Stopping keyboard interception + Stopping mouse interception + Stopping process monitoring + Stopping window monitoring + Terminating browser + Terminating taskbar + Waiting for Windows explorer to start up + Waiting for Windows explorer to shut down + Plugged in, charging... (%%CHARGE%%%) + Fully charged (%%CHARGE%%%) + The battery charge is critically low. Please connect your computer to a power supply! + The battery charge is getting low. Consider connecting your computer to a power supply in time... + %%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%) + Click to choose a different keyboard layout... + Version \ No newline at end of file diff --git a/SafeExamBrowser.SystemComponents/KeyboardLayout.cs b/SafeExamBrowser.SystemComponents/KeyboardLayout.cs new file mode 100644 index 00000000..852915bd --- /dev/null +++ b/SafeExamBrowser.SystemComponents/KeyboardLayout.cs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.SystemComponents; +using SafeExamBrowser.Contracts.UserInterface.Taskbar; + +namespace SafeExamBrowser.SystemComponents +{ + public class KeyboardLayout : ISystemComponent + { + private IList layouts = new List(); + private ILogger logger; + private InputLanguage originalLanguage; + private ISystemKeyboardLayoutControl control; + private IText text; + + public KeyboardLayout(ILogger logger, IText text) + { + this.logger = logger; + this.text = text; + } + + public void Initialize(ISystemKeyboardLayoutControl control) + { + this.control = control; + + originalLanguage = InputLanguage.CurrentInputLanguage; + control.LayoutSelected += Control_LayoutSelected; + control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip)); + + logger.Info($"Saved current keyboard layout {ToString(originalLanguage)}."); + + foreach (InputLanguage language in InputLanguage.InstalledInputLanguages) + { + var layout = new KeyboardLayoutDefinition + { + CultureCode = language.Culture.ThreeLetterISOLanguageName.ToUpper(), + IsCurrent = originalLanguage.Equals(language), + Language = language, + Name = language.LayoutName + }; + + control.Add(layout); + layouts.Add(layout); + + logger.Info($"Added keyboard layout {ToString(language)} to system control."); + } + } + + public void Terminate() + { + control?.Close(); + + if (originalLanguage != null) + { + InputLanguage.CurrentInputLanguage = originalLanguage; + logger.Info($"Restored original keyboard layout {ToString(originalLanguage)}."); + } + } + + private void Control_LayoutSelected(IKeyboardLayout layout) + { + var language = layouts.First(l => l.Id == layout.Id).Language; + + InputLanguage.CurrentInputLanguage = language; + + foreach (var l in layouts) + { + l.IsCurrent = l.Id == layout.Id; + } + + logger.Info($"Changed keyboard layout to {ToString(language)}."); + } + + private string ToString(InputLanguage language) + { + return $"'{language.LayoutName}' ({language.Culture.ThreeLetterISOLanguageName.ToUpper()})"; + } + } +} diff --git a/SafeExamBrowser.SystemComponents/KeyboardLayoutDefinition.cs b/SafeExamBrowser.SystemComponents/KeyboardLayoutDefinition.cs new file mode 100644 index 00000000..e143be74 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/KeyboardLayoutDefinition.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Windows.Forms; +using SafeExamBrowser.Contracts.SystemComponents; + +namespace SafeExamBrowser.SystemComponents +{ + internal class KeyboardLayoutDefinition : IKeyboardLayout + { + internal InputLanguage Language { get; set; } + + public string CultureCode { get; set; } + public Guid Id { get; } + public bool IsCurrent { get; set; } + public string Name { get; set; } + + public KeyboardLayoutDefinition() + { + Id = Guid.NewGuid(); + } + } +} diff --git a/SafeExamBrowser.SystemComponents/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply.cs index 6e7804a4..d0c78ab4 100644 --- a/SafeExamBrowser.SystemComponents/PowerSupply.cs +++ b/SafeExamBrowser.SystemComponents/PowerSupply.cs @@ -47,11 +47,6 @@ namespace SafeExamBrowser.SystemComponents logger.Info("Started monitoring the power supply."); } - private void Timer_Elapsed(object sender, ElapsedEventArgs e) - { - UpdateControl(); - } - public void Terminate() { timer?.Stop(); @@ -59,6 +54,11 @@ namespace SafeExamBrowser.SystemComponents logger.Info("Stopped monitoring the power supply."); } + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + UpdateControl(); + } + private void UpdateControl() { var charge = SystemInformation.PowerStatus.BatteryLifePercent; diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index 80d5eb7f..549b1e6b 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -53,6 +53,8 @@ + + diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/ApplicationButton.xaml.cs b/SafeExamBrowser.UserInterface.Classic/Controls/ApplicationButton.xaml.cs index f415ee7b..2ee2a771 100644 --- a/SafeExamBrowser.UserInterface.Classic/Controls/ApplicationButton.xaml.cs +++ b/SafeExamBrowser.UserInterface.Classic/Controls/ApplicationButton.xaml.cs @@ -53,7 +53,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1; Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver; - InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false || IsMouseOver; + InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = IsMouseOver; InstancePopup.Opened += (o, args) => { diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml new file mode 100644 index 00000000..bfde3547 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml.cs b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml.cs new file mode 100644 index 00000000..1616fe9f --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutButton.xaml.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System.Windows; +using System.Windows.Controls; + +namespace SafeExamBrowser.UserInterface.Classic.Controls +{ + public partial class KeyboardLayoutButton : UserControl + { + public event RoutedEventHandler Click; + + public string CultureCode + { + set { CultureCodeTextBlock.Text = value; } + } + + public bool IsCurrent + { + set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; } + } + + public string LayoutName + { + set { LayoutNameTextBlock.Text = value; } + } + + public KeyboardLayoutButton() + { + InitializeComponent(); + + Button.Click += (o, args) => Click?.Invoke(o, args); + } + } +} diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml new file mode 100644 index 00000000..f1a96092 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + 5 + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml.cs b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml.cs new file mode 100644 index 00000000..64f97bc1 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/KeyboardLayoutControl.xaml.cs @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Linq; +using System.Windows.Controls; +using System.Windows.Media; +using SafeExamBrowser.Contracts.SystemComponents; +using SafeExamBrowser.Contracts.UserInterface.Taskbar; + +namespace SafeExamBrowser.UserInterface.Classic.Controls +{ + public partial class KeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl + { + public event KeyboardLayoutSelectedEventHandler LayoutSelected; + + public KeyboardLayoutControl() + { + InitializeComponent(); + InitializeKeyboardLayoutControl(); + } + + public void Add(IKeyboardLayout layout) + { + var button = new KeyboardLayoutButton(); + + button.Click += (o, args) => + { + SetCurrent(button, layout); + Popup.IsOpen = false; + LayoutSelected?.Invoke(layout); + }; + button.CultureCode = layout.CultureCode; + button.LayoutName = layout.Name; + + LayoutsStackPanel.Children.Add(button); + + if (layout.IsCurrent) + { + SetCurrent(button, layout); + } + } + + public void Close() + { + Popup.IsOpen = false; + } + + public void SetTooltip(string text) + { + Button.ToolTip = text; + } + + private void InitializeKeyboardLayoutControl() + { + var originalBrush = Button.Background; + + Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; + Button.MouseLeave += (o, args) => Popup.IsOpen = Popup.IsMouseOver; + Popup.MouseLeave += (o, args) => Popup.IsOpen = IsMouseOver; + + Popup.Opened += (o, args) => + { + Background = Brushes.LightBlue; + Button.Background = Brushes.LightBlue; + }; + + Popup.Closed += (o, args) => + { + Background = originalBrush; + Button.Background = originalBrush; + }; + } + + private void SetCurrent(KeyboardLayoutButton button, IKeyboardLayout layout) + { + var name = layout.Name?.Length > 3 ? String.Join(string.Empty, layout.Name.Split(' ').Select(s => s.First())) : layout.Name; + + foreach (var child in LayoutsStackPanel.Children) + { + if (child is KeyboardLayoutButton) + { + (child as KeyboardLayoutButton).IsCurrent = false; + } + } + + button.IsCurrent = true; + LayoutCultureCode.Text = layout.CultureCode; + LayoutName.Text = name; + } + } +} diff --git a/SafeExamBrowser.UserInterface.Classic/Images/Reload.xaml b/SafeExamBrowser.UserInterface.Classic/Images/Reload.xaml index 0418cca3..eb620713 100644 --- a/SafeExamBrowser.UserInterface.Classic/Images/Reload.xaml +++ b/SafeExamBrowser.UserInterface.Classic/Images/Reload.xaml @@ -1,4 +1,4 @@ - diff --git a/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj b/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj index 577c295d..771a7603 100644 --- a/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj +++ b/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj @@ -78,6 +78,12 @@ DateTimeControl.xaml + + KeyboardLayoutButton.xaml + + + KeyboardLayoutControl.xaml + NotificationButton.xaml @@ -120,6 +126,14 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs index bbd1b9c2..24741efa 100644 --- a/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs @@ -65,6 +65,11 @@ namespace SafeExamBrowser.UserInterface.Classic return new NotificationButton(info); } + public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl() + { + return new KeyboardLayoutControl(); + } + public ISystemPowerSupplyControl CreatePowerSupplyControl() { return new PowerSupplyControl(); diff --git a/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs index 2b1c9296..227139ca 100644 --- a/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs @@ -65,6 +65,12 @@ namespace SafeExamBrowser.UserInterface.Windows10 return new NotificationButton(info); } + public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl() + { + // TODO + throw new System.NotImplementedException(); + } + public ISystemPowerSupplyControl CreatePowerSupplyControl() { return new PowerSupplyControl(); diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 598266d7..80d4cafd 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -47,6 +47,7 @@ namespace SafeExamBrowser private IProcessMonitor processMonitor; private IRuntimeController runtimeController; private ISettings settings; + private ISystemComponent keyboardLayout; private ISystemComponent powerSupply; private ISystemInfo systemInfo; private IText text; @@ -77,6 +78,7 @@ namespace SafeExamBrowser browserController = new BrowserApplicationController(settings, text, uiFactory); displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods); keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor))); + keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, typeof(KeyboardLayout)), text); mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse); powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text); processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); @@ -91,7 +93,7 @@ namespace SafeExamBrowser StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor)); StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor)); StartupOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar)); - StartupOperations.Enqueue(new TaskbarOperation(logger, settings, powerSupply, systemInfo, Taskbar, text, uiFactory)); + StartupOperations.Enqueue(new TaskbarOperation(logger, settings, keyboardLayout, powerSupply, systemInfo, Taskbar, text, uiFactory)); StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory)); StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger)); StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods));