SEBWIN-110: Implemented a basic version of the keyboard layout system control.

This commit is contained in:
dbuechel 2017-09-13 08:33:12 +02:00
parent 189d06ef20
commit da7e65e7e8
23 changed files with 514 additions and 57 deletions

View file

@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration
} }
public bool AllowApplicationLog => true; public bool AllowApplicationLog => true;
public bool AllowKeyboardLayout => true;
public string AppDataFolderName => "SafeExamBrowser"; public string AppDataFolderName => "SafeExamBrowser";
public string ApplicationLogFile public string ApplicationLogFile
@ -37,9 +37,7 @@ namespace SafeExamBrowser.Configuration
} }
public IBrowserSettings Browser { get; private set; } public IBrowserSettings Browser { get; private set; }
public IKeyboardSettings Keyboard { get; private set; } public IKeyboardSettings Keyboard { get; private set; }
public IMouseSettings Mouse { get; private set; } public IMouseSettings Mouse { get; private set; }
public string LogFolderPath public string LogFolderPath

View file

@ -15,6 +15,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary> /// </summary>
bool AllowApplicationLog { get; } bool AllowApplicationLog { get; }
/// <summary>
/// Determines whether the user may switch the keyboard layout during runtime.
/// </summary>
bool AllowKeyboardLayout { get; }
/// <summary> /// <summary>
/// The name used for the application data folder. /// The name used for the application data folder.
/// </summary> /// </summary>

View file

@ -41,6 +41,7 @@ namespace SafeExamBrowser.Contracts.I18n
SplashScreen_StopProcessMonitoring, SplashScreen_StopProcessMonitoring,
SplashScreen_StopWindowMonitoring, SplashScreen_StopWindowMonitoring,
SplashScreen_TerminateBrowser, SplashScreen_TerminateBrowser,
SplashScreen_TerminateTaskbar,
SplashScreen_WaitExplorerStartup, SplashScreen_WaitExplorerStartup,
SplashScreen_WaitExplorerTermination, SplashScreen_WaitExplorerTermination,
SystemControl_BatteryCharged, SystemControl_BatteryCharged,
@ -48,6 +49,7 @@ namespace SafeExamBrowser.Contracts.I18n
SystemControl_BatteryChargeCriticalWarning, SystemControl_BatteryChargeCriticalWarning,
SystemControl_BatteryChargeLowInfo, SystemControl_BatteryChargeLowInfo,
SystemControl_BatteryRemainingCharge, SystemControl_BatteryRemainingCharge,
SystemControl_KeyboardLayoutTooltip,
Version Version
} }
} }

View file

@ -87,12 +87,14 @@
<Compile Include="Monitoring\KeyState.cs" /> <Compile Include="Monitoring\KeyState.cs" />
<Compile Include="Monitoring\MouseButton.cs" /> <Compile Include="Monitoring\MouseButton.cs" />
<Compile Include="SystemComponents\BatteryChargeStatus.cs" /> <Compile Include="SystemComponents\BatteryChargeStatus.cs" />
<Compile Include="SystemComponents\IKeyboardLayout.cs" />
<Compile Include="SystemComponents\ISystemComponent.cs" /> <Compile Include="SystemComponents\ISystemComponent.cs" />
<Compile Include="UserInterface\IBrowserControl.cs" /> <Compile Include="UserInterface\IBrowserControl.cs" />
<Compile Include="UserInterface\IBrowserWindow.cs" /> <Compile Include="UserInterface\IBrowserWindow.cs" />
<Compile Include="UserInterface\IMessageBox.cs" /> <Compile Include="UserInterface\IMessageBox.cs" />
<Compile Include="UserInterface\Taskbar\INotificationButton.cs" /> <Compile Include="UserInterface\Taskbar\INotificationButton.cs" />
<Compile Include="UserInterface\ISplashScreen.cs" /> <Compile Include="UserInterface\ISplashScreen.cs" />
<Compile Include="UserInterface\Taskbar\ISystemKeyboardLayoutControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemPowerSupplyControl.cs" /> <Compile Include="UserInterface\Taskbar\ISystemPowerSupplyControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemControl.cs" /> <Compile Include="UserInterface\Taskbar\ISystemControl.cs" />
<Compile Include="UserInterface\Taskbar\ITaskbar.cs" /> <Compile Include="UserInterface\Taskbar\ITaskbar.cs" />

View file

@ -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
{
/// <summary>
/// The three-letter ISO code of the culture to which this keyboard layout is associated.
/// </summary>
string CultureCode { get; }
/// <summary>
/// The unique identifier of the keyboard layout.
/// </summary>
Guid Id { get; }
/// <summary>
/// Specifies whether this is the current keyboard layout.
/// </summary>
bool IsCurrent { get; }
/// <summary>
/// The name of this keyboard layout.
/// </summary>
string Name { get; }
}
}

View file

@ -41,6 +41,11 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// </summary> /// </summary>
INotificationButton CreateNotification(INotificationInfo info); INotificationButton CreateNotification(INotificationInfo info);
/// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer.
/// </summary>
ISystemKeyboardLayoutControl CreateKeyboardLayoutControl();
/// <summary> /// <summary>
/// Creates a system control displaying the power supply status of the computer. /// Creates a system control displaying the power supply status of the computer.
/// </summary> /// </summary>

View file

@ -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
{
/// <summary>
/// Event fired when the user selected a keyboard layout.
/// </summary>
event KeyboardLayoutSelectedEventHandler LayoutSelected;
/// <summary>
/// Adds the given layout to the list of selectable keyboard layouts.
/// </summary>
void Add(IKeyboardLayout layout);
}
}

View file

@ -21,8 +21,9 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public class TaskbarOperation : IOperation public class TaskbarOperation : IOperation
{ {
private ILogger logger; private ILogger logger;
private INotificationController aboutController, logController; private INotificationController logController;
private ISettings settings; private ISettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply; private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar; private ITaskbar taskbar;
@ -34,6 +35,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public TaskbarOperation( public TaskbarOperation(
ILogger logger, ILogger logger,
ISettings settings, ISettings settings,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply, ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemInfo systemInfo, ISystemInfo systemInfo,
ITaskbar taskbar, ITaskbar taskbar,
@ -42,6 +44,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
this.logger = logger; this.logger = logger;
this.settings = settings; this.settings = settings;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply; this.powerSupply = powerSupply;
this.systemInfo = systemInfo; this.systemInfo = systemInfo;
this.taskbar = taskbar; this.taskbar = taskbar;
@ -59,16 +62,31 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
CreateLogNotification(); CreateLogNotification();
} }
if (settings.AllowKeyboardLayout)
{
AddKeyboardLayoutControl();
}
if (systemInfo.HasBattery) if (systemInfo.HasBattery)
{ {
CreatePowerSupplyComponent(); AddPowerSupplyControl();
} }
} }
public void Revert() public void Revert()
{ {
logController?.Terminate(); logger.Info("Terminating taskbar...");
aboutController?.Terminate(); SplashScreen.UpdateText(TextKey.SplashScreen_TerminateTaskbar);
if (settings.AllowApplicationLog)
{
logController?.Terminate();
}
if (settings.AllowKeyboardLayout)
{
keyboardLayout.Terminate();
}
if (systemInfo.HasBattery) 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() private void CreateLogNotification()
{ {
var logInfo = new LogNotificationInfo(text); var logInfo = new LogNotificationInfo(text);
@ -86,13 +120,5 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
taskbar.AddNotification(logNotification); taskbar.AddNotification(logNotification);
} }
private void CreatePowerSupplyComponent()
{
var control = uiFactory.CreatePowerSupplyControl();
powerSupply.Initialize(control);
taskbar.AddSystemControl(control);
}
} }
} }

View file

@ -1,37 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Text> <Text>
<Browser_ShowDeveloperConsole>Open Console</Browser_ShowDeveloperConsole> <Browser_ShowDeveloperConsole>Open Console</Browser_ShowDeveloperConsole>
<LogWindow_Title>Application Log</LogWindow_Title> <LogWindow_Title>Application Log</LogWindow_Title>
<MessageBox_ShutdownError>An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...</MessageBox_ShutdownError> <MessageBox_ShutdownError>An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...</MessageBox_ShutdownError>
<MessageBox_ShutdownErrorTitle>Shutdown Error</MessageBox_ShutdownErrorTitle> <MessageBox_ShutdownErrorTitle>Shutdown Error</MessageBox_ShutdownErrorTitle>
<MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError> <MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError>
<MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle> <MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle>
<Notification_AboutTooltip>About Safe Exam Browser</Notification_AboutTooltip> <Notification_AboutTooltip>About Safe Exam Browser</Notification_AboutTooltip>
<Notification_LogTooltip>Application Log</Notification_LogTooltip> <Notification_LogTooltip>Application Log</Notification_LogTooltip>
<SplashScreen_EmptyClipboard>Emptying clipboard</SplashScreen_EmptyClipboard> <SplashScreen_EmptyClipboard>Emptying clipboard</SplashScreen_EmptyClipboard>
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser> <SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring> <SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar> <SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
<SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring> <SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring>
<SplashScreen_InitializeWorkingArea>Initializing working area</SplashScreen_InitializeWorkingArea> <SplashScreen_InitializeWorkingArea>Initializing working area</SplashScreen_InitializeWorkingArea>
<SplashScreen_RestoreWorkingArea>Restoring working area</SplashScreen_RestoreWorkingArea> <SplashScreen_RestoreWorkingArea>Restoring working area</SplashScreen_RestoreWorkingArea>
<SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure> <SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure>
<SplashScreen_StartEventHandling>Starting event handling</SplashScreen_StartEventHandling> <SplashScreen_StartEventHandling>Starting event handling</SplashScreen_StartEventHandling>
<SplashScreen_StartKeyboardInterception>Starting keyboard interception</SplashScreen_StartKeyboardInterception> <SplashScreen_StartKeyboardInterception>Starting keyboard interception</SplashScreen_StartKeyboardInterception>
<SplashScreen_StartMouseInterception>Starting mouse interception</SplashScreen_StartMouseInterception> <SplashScreen_StartMouseInterception>Starting mouse interception</SplashScreen_StartMouseInterception>
<SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure> <SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure>
<SplashScreen_StopEventHandling>Stopping event handling</SplashScreen_StopEventHandling> <SplashScreen_StopEventHandling>Stopping event handling</SplashScreen_StopEventHandling>
<SplashScreen_StopKeyboardInterception>Stopping keyboard interception</SplashScreen_StopKeyboardInterception> <SplashScreen_StopKeyboardInterception>Stopping keyboard interception</SplashScreen_StopKeyboardInterception>
<SplashScreen_StopMouseInterception>Stopping mouse interception</SplashScreen_StopMouseInterception> <SplashScreen_StopMouseInterception>Stopping mouse interception</SplashScreen_StopMouseInterception>
<SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring> <SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring>
<SplashScreen_StopWindowMonitoring>Stopping window monitoring</SplashScreen_StopWindowMonitoring> <SplashScreen_StopWindowMonitoring>Stopping window monitoring</SplashScreen_StopWindowMonitoring>
<SplashScreen_TerminateBrowser>Terminating browser</SplashScreen_TerminateBrowser> <SplashScreen_TerminateBrowser>Terminating browser</SplashScreen_TerminateBrowser>
<SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup> <SplashScreen_TerminateTaskbar>Terminating taskbar</SplashScreen_TerminateTaskbar>
<SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination> <SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup>
<SystemControl_BatteryCharging>Plugged in, charging... (%%CHARGE%%%)</SystemControl_BatteryCharging> <SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination>
<SystemControl_BatteryCharged>Fully charged (%%CHARGE%%%)</SystemControl_BatteryCharged> <SystemControl_BatteryCharging>Plugged in, charging... (%%CHARGE%%%)</SystemControl_BatteryCharging>
<SystemControl_BatteryChargeCriticalWarning>The battery charge is critically low. Please connect your computer to a power supply!</SystemControl_BatteryChargeCriticalWarning> <SystemControl_BatteryCharged>Fully charged (%%CHARGE%%%)</SystemControl_BatteryCharged>
<SystemControl_BatteryChargeLowInfo>The battery charge is getting low. Consider connecting your computer to a power supply in time...</SystemControl_BatteryChargeLowInfo> <SystemControl_BatteryChargeCriticalWarning>The battery charge is critically low. Please connect your computer to a power supply!</SystemControl_BatteryChargeCriticalWarning>
<SystemControl_BatteryRemainingCharge>%%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)</SystemControl_BatteryRemainingCharge> <SystemControl_BatteryChargeLowInfo>The battery charge is getting low. Consider connecting your computer to a power supply in time...</SystemControl_BatteryChargeLowInfo>
<Version>Version</Version> <SystemControl_BatteryRemainingCharge>%%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)</SystemControl_BatteryRemainingCharge>
<SystemControl_KeyboardLayoutTooltip>Click to choose a different keyboard layout...</SystemControl_KeyboardLayoutTooltip>
<Version>Version</Version>
</Text> </Text>

View file

@ -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<ISystemKeyboardLayoutControl>
{
private IList<KeyboardLayoutDefinition> layouts = new List<KeyboardLayoutDefinition>();
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()})";
}
}
}

View file

@ -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();
}
}
}

View file

@ -47,11 +47,6 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Started monitoring the power supply."); logger.Info("Started monitoring the power supply.");
} }
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
}
public void Terminate() public void Terminate()
{ {
timer?.Stop(); timer?.Stop();
@ -59,6 +54,11 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Stopped monitoring the power supply."); logger.Info("Stopped monitoring the power supply.");
} }
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
}
private void UpdateControl() private void UpdateControl()
{ {
var charge = SystemInformation.PowerStatus.BatteryLifePercent; var charge = SystemInformation.PowerStatus.BatteryLifePercent;

View file

@ -53,6 +53,8 @@
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="KeyboardLayoutDefinition.cs" />
<Compile Include="KeyboardLayout.cs" />
<Compile Include="PowerSupply.cs" /> <Compile Include="PowerSupply.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -53,7 +53,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1; Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver; 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) => InstancePopup.Opened += (o, args) =>
{ {

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Classic.Controls.KeyboardLayoutButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="LayoutNameTextBlock" Grid.Column="2" Foreground="Gray" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -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);
}
}
}

View file

@ -0,0 +1,40 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Classic.Controls.KeyboardLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.5,0.5,0.5,0">
<ScrollViewer x:Name="LayoutsScrollViewer" Background="{StaticResource BackgroundBrush}" MaxHeight="250" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" HorizontalContentAlignment="Stretch" Template="{StaticResource TaskbarButton}"
VerticalContentAlignment="Stretch" Width="40">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="LayoutCultureCode" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<TextBlock x:Name="LayoutName" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -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;
}
}
}

View file

@ -1,4 +1,4 @@
<Viewbox Width="1024.000" Height="1024.000" <Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000"> <Canvas Width="1024.000" Height="1024.000">

View file

@ -78,6 +78,12 @@
<Compile Include="Controls\DateTimeControl.xaml.cs"> <Compile Include="Controls\DateTimeControl.xaml.cs">
<DependentUpon>DateTimeControl.xaml</DependentUpon> <DependentUpon>DateTimeControl.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\KeyboardLayoutButton.xaml.cs">
<DependentUpon>KeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\KeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\NotificationButton.xaml.cs"> <Compile Include="Controls\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon> <DependentUpon>NotificationButton.xaml</DependentUpon>
</Compile> </Compile>
@ -120,6 +126,14 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\KeyboardLayoutButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\KeyboardLayoutControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\NotificationButton.xaml"> <Page Include="Controls\NotificationButton.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View file

@ -65,6 +65,11 @@ namespace SafeExamBrowser.UserInterface.Classic
return new NotificationButton(info); return new NotificationButton(info);
} }
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
{
return new KeyboardLayoutControl();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl() public ISystemPowerSupplyControl CreatePowerSupplyControl()
{ {
return new PowerSupplyControl(); return new PowerSupplyControl();

View file

@ -65,6 +65,12 @@ namespace SafeExamBrowser.UserInterface.Windows10
return new NotificationButton(info); return new NotificationButton(info);
} }
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
{
// TODO
throw new System.NotImplementedException();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl() public ISystemPowerSupplyControl CreatePowerSupplyControl()
{ {
return new PowerSupplyControl(); return new PowerSupplyControl();

View file

@ -47,6 +47,7 @@ namespace SafeExamBrowser
private IProcessMonitor processMonitor; private IProcessMonitor processMonitor;
private IRuntimeController runtimeController; private IRuntimeController runtimeController;
private ISettings settings; private ISettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply; private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private IText text; private IText text;
@ -77,6 +78,7 @@ namespace SafeExamBrowser
browserController = new BrowserApplicationController(settings, text, uiFactory); browserController = new BrowserApplicationController(settings, text, uiFactory);
displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods); displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor))); 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); mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text); powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text);
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
@ -91,7 +93,7 @@ namespace SafeExamBrowser
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor)); StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor)); StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
StartupOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar)); 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 BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger)); StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods)); StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods));