SEBWIN-223, SEBWIN-224: Implemented user interface and browser configuration mapping.

This commit is contained in:
dbuechel 2019-01-11 08:25:40 +01:00
parent b4839641d4
commit 704aea3a45
59 changed files with 224 additions and 2204 deletions

View file

@ -116,7 +116,8 @@ namespace SafeExamBrowser.Browser
{
CachePath = appConfig.BrowserCachePath,
LogFile = appConfig.BrowserLogFile,
LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info)
LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info),
UserAgent = settings.UseCustomUserAgent ? settings.CustomUserAgent : string.Empty
};
logger.Debug($"CEF cache path is '{cefSettings.CachePath}'.");

View file

@ -65,12 +65,11 @@ namespace SafeExamBrowser.Browser
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
control = new BrowserControl(appConfig, settings, controlLogger, text);
control = new BrowserControl(appConfig, settings, downloadHandler, controlLogger, text);
control.AddressChanged += Control_AddressChanged;
control.LoadingStateChanged += Control_LoadingStateChanged;
control.TitleChanged += Control_TitleChanged;
(control as BrowserControl).DownloadHandler = downloadHandler;
(control as BrowserControl).Initialize();
control.Initialize();
logger.Debug("Initialized browser control.");

View file

@ -23,6 +23,7 @@ namespace SafeExamBrowser.Browser
{
private AppConfig appConfig;
private BrowserSettings settings;
private IDownloadHandler downloadHandler;
private ILogger logger;
private IText text;
@ -48,9 +49,15 @@ namespace SafeExamBrowser.Browser
remove { titleChanged -= value; }
}
public BrowserControl(AppConfig appConfig, BrowserSettings settings, ILogger logger, IText text) : base(settings.StartUrl)
public BrowserControl(
AppConfig appConfig,
BrowserSettings settings,
IDownloadHandler downloadHandler,
ILogger logger,
IText text) : base(settings.StartUrl)
{
this.appConfig = appConfig;
this.downloadHandler = downloadHandler;
this.logger = logger;
this.settings = settings;
this.text = text;
@ -62,6 +69,7 @@ namespace SafeExamBrowser.Browser
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
DownloadHandler = downloadHandler;
KeyboardHandler = new KeyboardHandler(settings);
MenuHandler = new ContextMenuHandler(settings, text);
RequestHandler = new RequestHandler(appConfig);

View file

@ -74,6 +74,7 @@ namespace SafeExamBrowser.Client.Operations
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeTaskbar);
AddAboutNotification();
taskbar.ShowClock = settings.ShowClock;
if (settings.AllowApplicationLog)
{

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019 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 SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapMainWindowMode(Settings settings, object value)
{
const int FULLSCREEN = 1;
if (value is int mode)
{
settings.Browser.FullScreenMode = mode == FULLSCREEN;
}
}
private void MapUserAgentMode(IDictionary<string, object> rawData, Settings settings)
{
const int DEFAULT = 0;
var useCustomForDesktop = rawData.TryGetValue(Keys.Browser.UserAgentModeDesktop, out var value) && value as int? != DEFAULT;
var useCustomForMobile = rawData.TryGetValue(Keys.Browser.UserAgentModeMobile, out value) && value as int? != DEFAULT;
if (settings.UserInterfaceMode == UserInterfaceMode.Desktop && useCustomForDesktop)
{
settings.Browser.UseCustomUserAgent = true;
settings.Browser.CustomUserAgent = rawData[Keys.Browser.CustomUserAgentDesktop] as string;
}
else if (settings.UserInterfaceMode == UserInterfaceMode.Mobile && useCustomForMobile)
{
settings.Browser.UseCustomUserAgent = true;
settings.Browser.CustomUserAgent = rawData[Keys.Browser.CustomUserAgentMobile] as string;
}
}
private void MapZoomMode(Settings settings, object value)
{
const int PAGE = 0;
if (value is int mode)
{
// TODO: settings.Browser.ZoomMode = mode == PAGE ? BrowserZoomMode.Page : BrowserZoomMode.Text;
}
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019 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.Configuration.Settings;
namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapClock(Settings settings, object value)
{
if (value is bool show)
{
settings.Taskbar.ShowClock = show;
}
}
private void MapKeyboardLayout(Settings settings, object value)
{
if (value is bool enabled)
{
settings.Taskbar.AllowKeyboardLayout = enabled;
}
}
private void MapWirelessNetwork(Settings settings, object value)
{
if (value is bool enabled)
{
settings.Taskbar.AllowWirelessNetwork = enabled;
}
}
private void MapUserInterfaceMode(Settings settings, object value)
{
if (value is bool mobile)
{
settings.UserInterfaceMode = mobile ? UserInterfaceMode.Mobile : UserInterfaceMode.Desktop;
}
}
}
}

View file

@ -19,12 +19,26 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
Map(item.Key, item.Value, settings);
}
MapUserAgentMode(rawData, settings);
}
private void Map(string key, object value, Settings settings)
{
switch (key)
{
case Keys.Browser.EnablePageZoom:
// TODO: MapPageZoom(settings, value);
break;
case Keys.Browser.EnableTextZoom:
// TODO: MapTextZoom(settings, value);
break;
case Keys.Browser.MainWindowMode:
MapMainWindowMode(settings, value);
break;
case Keys.Browser.ZoomMode:
MapZoomMode(settings, value);
break;
case Keys.ConfigurationFile.ConfigurationPurpose:
MapConfigurationMode(settings, value);
break;
@ -97,6 +111,18 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
case Keys.Input.Mouse.EnableRightMouse:
MapEnableRightMouse(settings, value);
break;
case Keys.UserInterface.ShowClock:
MapClock(settings, value);
break;
case Keys.UserInterface.AllowKeyboardLayout:
MapKeyboardLayout(settings, value);
break;
case Keys.UserInterface.AllowWirelessNetwork:
MapWirelessNetwork(settings, value);
break;
case Keys.UserInterface.UserInterfaceMode:
MapUserInterfaceMode(settings, value);
break;
}
}
}

View file

@ -128,6 +128,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Taskbar.AllowApplicationLog = false;
settings.Taskbar.AllowKeyboardLayout = true;
settings.Taskbar.AllowWirelessNetwork = false;
settings.Taskbar.ShowClock = true;
// TODO: Default values for alpha version only, remove for final release!
settings.Browser.AllowAddressBar = true;

View file

@ -20,6 +20,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal static class Browser
{
internal const string CustomUserAgentDesktop = "browserUserAgentWinDesktopModeCustom";
internal const string CustomUserAgentMobile = "browserUserAgentWinTouchModeCustom";
internal const string EnablePageZoom = "enableZoomPage";
internal const string EnableTextZoom = "enableZoomText";
internal const string MainWindowMode = "browserViewMode";
internal const string UserAgentModeDesktop = "browserUserAgentWinDesktopMode";
internal const string UserAgentModeMobile = "browserUserAgentWinTouchMode";
internal const string ZoomMode = "zoomMode";
}
internal static class ConfigurationFile
@ -90,6 +98,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal static class UserInterface
{
internal const string AllowKeyboardLayout = "showInputLanguage";
internal const string AllowWirelessNetwork = "allowWlan";
internal const string ShowClock = "showTime";
internal const string UserInterfaceMode = "touchOptimized";
}
}
}

View file

@ -69,6 +69,9 @@
<Compile Include="DataFormats\BinarySerializer.cs" />
<Compile Include="DataFormats\BinaryBlock.cs" />
<Compile Include="ConfigurationData\DataMapper.cs" />
<Compile Include="ConfigurationData\DataMapper.Browser.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>
<Compile Include="ConfigurationData\DataMapper.ConfigurationFile.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>
@ -78,6 +81,9 @@
<Compile Include="ConfigurationData\DataMapper.Input.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>
<Compile Include="ConfigurationData\DataMapper.UserInterface.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>
<Compile Include="Cryptography\HashAlgorithm.cs" />
<Compile Include="DataFormats\XmlElement.cs" />
<Compile Include="DataFormats\XmlParser.cs" />

View file

@ -51,6 +51,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary>
public bool AllowReloading { get; set; }
/// <summary>
/// The custom user agent to optionally be used for all requests.
/// </summary>
public string CustomUserAgent { get; set; }
/// <summary>
/// Determines whether the main browser window should be rendered in fullscreen mode, i.e. without window frame.
/// </summary>
@ -60,5 +65,10 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// The start URL with which a new browser window should be loaded.
/// </summary>
public string StartUrl { get; set; }
/// <summary>
/// Determines whether a custom user agent should be used for all requests, see <see cref="CustomUserAgent"/>.
/// </summary>
public bool UseCustomUserAgent { get; set; }
}
}

View file

@ -61,6 +61,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary>
public TaskbarSettings Taskbar { get; set; }
/// <summary>
/// The mode which determines the look & feel of the user interface.
/// </summary>
public UserInterfaceMode UserInterfaceMode { get; set; }
public Settings()
{
Browser = new BrowserSettings();

View file

@ -30,5 +30,10 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// Determines whether the user may control the wireless network connection during runtime.
/// </summary>
public bool AllowWirelessNetwork { get; set; }
/// <summary>
/// Determines whether the current date and time will be rendered in the taskbar.
/// </summary>
public bool ShowClock { get; set; }
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2019 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/.
*/
namespace SafeExamBrowser.Contracts.Configuration.Settings
{
/// <summary>
/// Defines all possible look & feel options for the application.
/// </summary>
public enum UserInterfaceMode
{
/// <summary>
/// In this mode, the user interface is optimized for desktop computers with keyboard and mouse.
/// </summary>
Desktop,
/// <summary>
/// In this mode, the user interface is optimized for mobile computers with touch capability.
/// </summary>
Mobile
}
}

View file

@ -70,6 +70,7 @@
<Compile Include="Configuration\DataResources\IResourceLoader.cs" />
<Compile Include="Configuration\DataResources\IResourceSaver.cs" />
<Compile Include="Configuration\SaveStatus.cs" />
<Compile Include="Configuration\Settings\UserInterfaceMode.cs" />
<Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" />
<Compile Include="Core\Events\NameChangedEventHandler.cs" />
<Compile Include="Core\IApplicationController.cs" />

View file

@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.UserInterface.Browser
/// </summary>
event TitleChangedEventHandler TitleChanged;
/// <summary>
/// Initializes the browser control.
/// </summary>
void Initialize();
/// <summary>
/// Navigates to the previous page in the browser control history.
/// </summary>

View file

@ -16,6 +16,11 @@ namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
/// </summary>
public interface ITaskbar
{
/// <summary>
/// Controls the visibility of the clock.
/// </summary>
bool ShowClock { set; }
/// <summary>
/// Event fired when the user clicked the quit button in the taskbar.
/// </summary>

View file

@ -24,7 +24,7 @@
</ScrollViewer>
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<local:DateTimeControl Grid.Column="3" Foreground="DimGray" Padding="10,0,10,0" />
<local:DateTimeControl Grid.Column="3" x:Name="Clock" Foreground="DimGray" Padding="10,0,10,0" />
<local:QuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid>
</Window>

View file

@ -20,6 +20,11 @@ namespace SafeExamBrowser.UserInterface.Classic
private bool allowClose;
private ILogger logger;
public bool ShowClock
{
set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); }
}
public event QuitButtonClickedEventHandler QuitButtonClicked;
public Taskbar(ILogger logger)

View file

@ -1,41 +0,0 @@
<Window x:Class="SafeExamBrowser.UserInterface.Windows10.AboutWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Windows10"
mc:Ignorable="d"
Title="About Safe Exam Browser" Height="350" Width="450" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico"
ShowInTaskbar="False" WindowStartupLocation="CenterScreen">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Windows10;component/Images/SplashScreen.png" Margin="0,5,0,0" />
<TextBlock x:Name="VersionInfo" Grid.Row="0" Grid.Column="1" Foreground="DarkGray" Margin="25,70,50,10" TextWrapping="Wrap" />
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="MainText" Foreground="White" Margin="10" FontSize="10" TextWrapping="Wrap">
This application is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
with this application, You can obtain one at http://mozilla.org/MPL/2.0/.
<LineBreak />
<LineBreak />
<Bold><Underline>CefSharp (.NET bindings for the Chromium Embedded Framework)</Underline></Bold>
<LineBreak />
Copyright © 2010-2017 The CefSharp Authors. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>CEF (Chromium Embedded Framework)</Underline></Bold>
<LineBreak />
Copyright © 2008-2014 Marshall A. Greenblatt. Portions Copyright © 2006-2009 Google Inc. All rights reserved.
</TextBlock>
</ScrollViewer>
</Grid>
</Window>

View file

@ -1,53 +0,0 @@
/*
* Copyright (c) 2019 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.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.UserInterface.Windows10
{
public partial class AboutWindow : Window, IWindow
{
private AppConfig appConfig;
private IText text;
private WindowClosingEventHandler closing;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public AboutWindow(AppConfig appConfig, IText text)
{
this.appConfig = appConfig;
this.text = text;
InitializeComponent();
InitializeAboutWindow();
}
public void BringToForeground()
{
Activate();
}
private void InitializeAboutWindow()
{
Closing += (o, args) => closing?.Invoke();
VersionInfo.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {appConfig.ProgramVersion}") { FontStyle = FontStyles.Italic });
VersionInfo.Inlines.Add(new LineBreak());
VersionInfo.Inlines.Add(new LineBreak());
VersionInfo.Inlines.Add(new Run(appConfig.ProgramCopyright) { FontSize = 10 });
}
}
}

View file

@ -1,28 +0,0 @@
<Window x:Class="SafeExamBrowser.UserInterface.Windows10.BrowserWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Windows10"
mc:Ignorable="d"
Title="BrowserWindow" Height="500" Width="500" WindowState="Maximized" Icon=".\Images\Chromium.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" x:Name="UrlTextBox" Margin="5,5,2.5,5" Height="20" HorizontalAlignment="Stretch" VerticalContentAlignment="Center" />
<Button Grid.Column="1" x:Name="ReloadButton" Margin="2.5,5,2.5,5" HorizontalAlignment="Center" VerticalAlignment="Center">Reload</Button>
<Button Grid.Column="2" x:Name="BackButton" Margin="2.5,5,2.5,5" HorizontalAlignment="Center" VerticalAlignment="Center">Back</Button>
<Button Grid.Column="3" x:Name="ForwardButton" Margin="2.5,5,5,5" HorizontalAlignment="Center" VerticalAlignment="Center">Forward</Button>
</Grid>
<WindowsFormsHost Grid.Row="1" x:Name="BrowserControlHost" />
</Grid>
</Window>

View file

@ -1,144 +0,0 @@
/*
* Copyright (c) 2019 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;
namespace SafeExamBrowser.UserInterface.Windows10
{
public partial class BrowserWindow : Window // TODO, IBrowserWindow
{
//private bool isMainWindow;
//private BrowserSettings settings;
//public WindowClosingEventHandler closing;
//public bool IsMainWindow
//{
// get
// {
// return isMainWindow;
// }
// set
// {
// isMainWindow = value;
// ApplySettings();
// }
//}
//public event AddressChangedEventHandler AddressChanged;
//public event ActionRequestedEventHandler BackwardNavigationRequested;
//public event ActionRequestedEventHandler ForwardNavigationRequested;
//public event ActionRequestedEventHandler ReloadRequested;
//event WindowClosingEventHandler IWindow.Closing
//{
// add { closing += value; }
// remove { closing -= value; }
//}
//public BrowserWindow(IBrowserControl browserControl, BrowserSettings settings)
//{
// this.settings = settings;
// InitializeComponent();
// InitializeBrowserWindow(browserControl);
//}
//public void BringToForeground()
//{
// Dispatcher.Invoke(() =>
// {
// if (WindowState == WindowState.Minimized)
// {
// WindowState = WindowState.Normal;
// }
// Activate();
// });
//}
//public new void Close()
//{
// Dispatcher.Invoke(base.Close);
//}
//public new void Hide()
//{
// Dispatcher.Invoke(base.Hide);
//}
//public new void Show()
//{
// Dispatcher.Invoke(base.Show);
//}
//public void UpdateAddress(string url)
//{
// Dispatcher.Invoke(() => UrlTextBox.Text = url);
//}
//public void UpdateTitle(string title)
//{
// Dispatcher.Invoke(() => Title = title);
//}
//private void InitializeBrowserWindow(IBrowserControl browserControl)
//{
// if (browserControl is System.Windows.Forms.Control control)
// {
// BrowserControlHost.Child = control;
// }
// Closing += (o, args) => closing?.Invoke();
// KeyUp += BrowserWindow_KeyUp;
// UrlTextBox.KeyUp += UrlTextBox_KeyUp;
// ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
// BackButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
// ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
// ApplySettings();
//}
//private void BrowserWindow_KeyUp(object sender, KeyEventArgs e)
//{
// if (e.Key == Key.F5)
// {
// ReloadRequested?.Invoke();
// }
//}
//private void UrlTextBox_KeyUp(object sender, KeyEventArgs e)
//{
// if (e.Key == Key.Enter)
// {
// AddressChanged?.Invoke(UrlTextBox.Text);
// }
//}
//private void ApplySettings()
//{
// if (IsMainWindow && settings.FullScreenMode)
// {
// MaxHeight = SystemParameters.WorkArea.Height;
// ResizeMode = ResizeMode.NoResize;
// WindowState = WindowState.Maximized;
// WindowStyle = WindowStyle.None;
// }
// UrlTextBox.IsEnabled = settings.AllowAddressBar;
// ReloadButton.IsEnabled = settings.AllowReloading;
// ReloadButton.Visibility = settings.AllowReloading ? Visibility.Visible : Visibility.Collapsed;
// BackButton.IsEnabled = settings.AllowBackwardNavigation;
// BackButton.Visibility = settings.AllowBackwardNavigation ? Visibility.Visible : Visibility.Collapsed;
// ForwardButton.IsEnabled = settings.AllowForwardNavigation;
// ForwardButton.Visibility = settings.AllowForwardNavigation ? Visibility.Visible : Visibility.Collapsed;
//}
}
}

View file

@ -1,34 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.ApplicationButton"
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.Windows10.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="50">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="InstancePopup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}" AllowsTransparency="True">
<ScrollViewer x:Name="InstanceScrollViewer" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</ScrollViewer.Background>
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="InstanceStackPanel" />
</ScrollViewer>
</Popup>
<Button x:Name="Button" Background="#00000000" BorderThickness="0" Click="Button_Click" Padding="5"
Template="{StaticResource ResourceKey=TaskbarButton}" Width="50" />
<Grid Panel.ZIndex="10">
<Rectangle x:Name="ActiveBar" Height="2" Width="40" VerticalAlignment="Bottom" Fill="LightSteelBlue" Visibility="Collapsed" />
</Grid>
</Grid>
</UserControl>

View file

@ -1,105 +0,0 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.UserInterface.Windows10.Utilities;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class ApplicationButton : UserControl, IApplicationButton
{
private IApplicationInfo info;
private IList<IApplicationInstance> instances = new List<IApplicationInstance>();
public event ApplicationButtonClickedEventHandler Clicked;
public ApplicationButton(IApplicationInfo info)
{
this.info = info;
InitializeComponent();
InitializeApplicationButton();
}
public void RegisterInstance(IApplicationInstance instance)
{
var instanceButton = new ApplicationInstanceButton(instance, info);
instanceButton.Clicked += (id) => Clicked?.Invoke(id);
instance.Terminated += (id) => Instance_OnTerminated(id, instanceButton);
instances.Add(instance);
InstanceStackPanel.Children.Add(instanceButton);
ActiveBar.Visibility = Visibility.Visible;
}
private void InitializeApplicationButton()
{
Button.ToolTip = info.Tooltip;
Button.Content = IconResourceLoader.Load(info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
Button.MouseLeave += (o, args) => InstancePopup.IsOpen &= InstancePopup.IsMouseOver || ActiveBar.IsMouseOver;
ActiveBar.MouseLeave += (o, args) => InstancePopup.IsOpen &= InstancePopup.IsMouseOver || Button.IsMouseOver;
InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false;
InstancePopup.Opened += (o, args) =>
{
ActiveBar.Width = Double.NaN;
Background = (Brush) new BrushConverter().ConvertFrom("#2AFFFFFF");
};
InstancePopup.Closed += (o, args) =>
{
ActiveBar.Width = 40;
Background = (Brush) new BrushConverter().ConvertFrom("#00000000");
};
InstanceStackPanel.SizeChanged += (o, args) =>
{
if (instances.Count > 9)
{
InstanceScrollViewer.MaxHeight = InstanceScrollViewer.ActualHeight;
}
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (instances.Count <= 1)
{
Clicked?.Invoke(instances.FirstOrDefault()?.Id);
}
else
{
InstancePopup.IsOpen = true;
}
}
private void Instance_OnTerminated(InstanceIdentifier id, ApplicationInstanceButton instanceButton)
{
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
InstanceStackPanel.Children.Remove(instanceButton);
if (!instances.Any())
{
ActiveBar.Visibility = Visibility.Collapsed;
}
}
}
}

View file

@ -1,31 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.ApplicationInstanceButton"
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.Windows10.Controls"
mc:Ignorable="d" d:DesignWidth="250">
<Grid>
<Button x:Name="Button" BorderThickness="0" Click="Button_Click" Height="25">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonContent" Background="#00000000" Padding="5">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Left" ContentSource="Content" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#2AFFFFFF" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#10FFFFFF" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" />
<TextBlock x:Name="Text" Foreground="White" Padding="5,0,5,0" />
</StackPanel>
</Button>
</Grid>
</UserControl>

View file

@ -1,55 +0,0 @@
/*
* Copyright (c) 2019 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;
using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.UserInterface.Windows10.Utilities;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class ApplicationInstanceButton : UserControl
{
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationButtonClickedEventHandler Clicked;
public ApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{
this.info = info;
this.instance = instance;
InitializeComponent();
InitializeApplicationInstanceButton();
}
private void InitializeApplicationInstanceButton()
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance.Name;
Button.ToolTip = instance.Name;
instance.NameChanged += (name) =>
{
Dispatcher.Invoke(() =>
{
Text.Text = name;
Button.ToolTip = name;
});
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance.Id);
}
}
}

View file

@ -1,33 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.DateTimeControl"
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.Windows10.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="75">
<Button Background="#00000000" BorderThickness="0" ToolTip="{Binding Path=ToolTip}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="Content" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#2AFFFFFF" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#2AFFFFFF" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<Grid Margin="5,0">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Button>
</UserControl>

View file

@ -1,27 +0,0 @@
/*
* Copyright (c) 2019 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.Controls;
using SafeExamBrowser.UserInterface.Windows10.ViewModels;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class DateTimeControl : UserControl
{
private DateTimeViewModel model = new DateTimeViewModel();
public DateTimeControl()
{
InitializeComponent();
DataContext = model;
TimeTextBlock.DataContext = model;
DateTextBlock.DataContext = model;
}
}
}

View file

@ -1,17 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.NotificationButton"
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.Windows10.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="28">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Button x:Name="IconButton" Click="Icon_Click" Background="#00000000" BorderThickness="0" Padding="5,0"
Template="{StaticResource ResourceKey=TaskbarButton}" Width="28"/>
</UserControl>

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2019 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;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.UserInterface.Windows10.Utilities;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class NotificationButton : UserControl, INotificationButton
{
public event NotificationButtonClickedEventHandler Clicked;
public NotificationButton(INotificationInfo info)
{
InitializeComponent();
InitializeNotificationIcon(info);
}
private void Icon_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke();
}
private void InitializeNotificationIcon(INotificationInfo info)
{
IconButton.ToolTip = info.Tooltip;
IconButton.Content = IconResourceLoader.Load(info.IconResource);
}
}
}

View file

@ -1,53 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.PowerSupplyControl"
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.Windows10.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="28">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}" AllowsTransparency="True">
<Border BorderBrush="White" BorderThickness="0.5,0.5,0.5,0" MaxWidth="250" Padding="20,10,20,20">
<Border.Background>
<SolidColorBrush Color="#404040" Opacity="0.8" />
</Border.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Background="Transparent" Click="Button_Click" Cursor="Hand" FontWeight="Bold"
Foreground="White" Margin="0,0,0,5" Template="{StaticResource ResourceKey=TaskbarButton}" Width="20">X</Button>
</Grid>
<TextBlock Grid.Row="1" x:Name="PopupText" Foreground="White" TextWrapping="Wrap" />
</Grid>
</Border>
</Popup>
<Button x:Name="Button" Background="#00000000" BorderThickness="0" Foreground="White" Padding="5,0"
Template="{StaticResource ResourceKey=TaskbarButton}" Width="28">
<Viewbox Stretch="Uniform" Width="Auto">
<Canvas Height="18" Width="18">
<Path Stroke="White" Data="M3,6 H17 V9 H18 V11 H17 V9 H17 V14 H3 Z" Panel.ZIndex="2" />
<Rectangle x:Name="BatteryCharge" Canvas.Left="3.5" Canvas.Top="6" Fill="Green" Height="8" Width="13" />
<Grid x:Name="PowerPlug" Panel.ZIndex="3">
<Path Stroke="White" Fill="Black" Data="M2.5,14.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" Panel.ZIndex="2" />
<Path Stroke="Black" Data="M4,14.5 V10 Q6,10 6,5.5" Panel.ZIndex="1" />
</Grid>
<TextBlock x:Name="Warning" FontSize="18" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="7" Canvas.Top="-3.5" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
</Button>
</Grid>
</UserControl>

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2019 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;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class PowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
private double BATTERY_CHARGE_MAX_WIDTH;
public PowerSupplyControl()
{
InitializeComponent();
BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
}
public void Close()
{
Popup.IsOpen = false;
}
public void SetBatteryCharge(double charge, BatteryChargeStatus status)
{
Dispatcher.Invoke(() =>
{
var width = BATTERY_CHARGE_MAX_WIDTH * charge;
width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width;
width = width < 0 ? 0 : width;
BatteryCharge.Width = width;
BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill;
BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill;
Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
});
}
public void SetPowerGridConnection(bool connected)
{
Dispatcher.Invoke(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed);
}
public void SetTooltip(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}
public void ShowCriticalBatteryWarning(string warning)
{
Dispatcher.Invoke(() => ShowPopup(warning));
}
public void ShowLowBatteryInfo(string info)
{
Dispatcher.Invoke(() => ShowPopup(info));
}
private void ShowPopup(string text)
{
Popup.IsOpen = true;
PopupText.Text = text;
Background = (Brush) new BrushConverter().ConvertFrom("#2AFFFFFF");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Popup.IsOpen = false;
Background = (Brush) new BrushConverter().ConvertFrom("#00000000");
}
}
}

View file

@ -1,28 +0,0 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Windows10.Controls.QuitButton"
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.Windows10.Controls"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="40">
<Grid>
<Button Click="Button_Click" Content="Quit" Background="#00000000" Foreground="White">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}" Padding="5">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Content" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#50FF0000" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#40FF0000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</UserControl>

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2019 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.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
namespace SafeExamBrowser.UserInterface.Windows10.Controls
{
public partial class QuitButton : UserControl
{
public event QuitButtonClickedEventHandler Clicked;
public QuitButton()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(new CancelEventArgs());
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View file

@ -1,18 +0,0 @@
<Window x:Class="SafeExamBrowser.UserInterface.Windows10.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Windows10"
mc:Ignorable="d"
Title="{Binding Path=WindowTitle}" Height="500" Width="1100" MinHeight="350" MinWidth="350" WindowStartupLocation="CenterScreen"
Icon="./Images/LogNotification.ico">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Grid>
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="LogContent" Background="Transparent" FontFamily="Consolas" Foreground="White" />
</ScrollViewer>
</Grid>
</Window>

View file

@ -1,89 +0,0 @@
/*
* Copyright (c) 2019 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.ComponentModel;
using System.Windows;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.UserInterface.Windows10.ViewModels;
namespace SafeExamBrowser.UserInterface.Windows10
{
public partial class LogWindow : Window, IWindow
{
private ILogger logger;
private LogViewModel model;
private WindowClosingEventHandler closing;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public LogWindow(ILogger logger, IText text)
{
InitializeComponent();
this.logger = logger;
this.model = new LogViewModel(text, ScrollViewer, LogContent);
InitializeLogWindow();
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
private void InitializeLogWindow()
{
DataContext = model;
Closing += LogWindow_Closing;
Loaded += LogWindow_Loaded;
}
private void LogWindow_Loaded(object sender, RoutedEventArgs e)
{
var log = logger.GetLog();
foreach (var content in log)
{
model.Notify(content);
}
logger.Subscribe(model);
logger.Info("Opened log window.");
}
private void LogWindow_Closing(object sender, CancelEventArgs e)
{
logger.Unsubscribe(model);
logger.Info("Closed log window.");
closing?.Invoke();
}
}
}

View file

@ -1,50 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SafeExamBrowser.UserInterface")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.UserInterface")]
[assembly: AssemblyCopyright("Copyright © 2019 ETH Zürich, Educational Development and Technology (LET)")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SafeExamBrowser.UserInterface.Windows10.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SafeExamBrowser.UserInterface.Windows10.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SafeExamBrowser.UserInterface.Windows10.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View file

@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View file

@ -1,194 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E1BE031A-4354-41E7-83E8-843DED4489FF}</ProjectGuid>
<OutputType>library</OutputType>
<RootNamespace>SafeExamBrowser.UserInterface.Windows10</RootNamespace>
<AssemblyName>SafeExamBrowser.UserInterface.Windows10</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Windows.Forms" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
<Compile Include="BrowserWindow.xaml.cs">
<DependentUpon>BrowserWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ApplicationInstanceButton.xaml.cs">
<DependentUpon>ApplicationInstanceButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\DateTimeControl.xaml.cs">
<DependentUpon>DateTimeControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ApplicationButton.xaml.cs">
<DependentUpon>ApplicationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\PowerSupplyControl.xaml.cs">
<DependentUpon>PowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="LogWindow.xaml.cs">
<DependentUpon>LogWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="UserInterfaceFactory.cs" />
<Compile Include="Utilities\IconResourceLoader.cs" />
<Compile Include="Utilities\VisualExtensions.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
<Compile Include="ViewModels\SplashScreenViewModel.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Page Include="AboutWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="BrowserWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ApplicationInstanceButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\DateTimeControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ApplicationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\NotificationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\PowerSupplyControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\QuitButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="LogWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SplashScreen.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Templates\Buttons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Taskbar.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj">
<Project>{47DA5933-BEF8-4729-94E6-ABDE2DB12262}</Project>
<Name>SafeExamBrowser.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SafeExamBrowser.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Chromium.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\LogNotification.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -1,30 +0,0 @@
<Window x:Class="SafeExamBrowser.UserInterface.Windows10.SplashScreen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="SplashScreen" Height="200" Width="350" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen"
Cursor="Wait" Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Topmost="True">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="155" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Windows10;component/Images/SplashScreen.png" />
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="White" Margin="10,75,10,10" TextWrapping="Wrap" />
</Grid>
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Minimum="0" Maximum="{Binding Path=MaxProgress}" Value="{Binding Path=CurrentProgress}" IsIndeterminate="{Binding Path=IsIndeterminate}" Background="#00000000" BorderThickness="0" />
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" Text="{Binding Path=Status}" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />
</Grid>
</Border>
</Window>

View file

@ -1,133 +0,0 @@
/*
* Copyright (c) 2019 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.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.UserInterface.Windows10.ViewModels;
namespace SafeExamBrowser.UserInterface.Windows10
{
public partial class SplashScreen : Window, ISplashScreen
{
private bool allowClose;
private SplashScreenViewModel model = new SplashScreenViewModel();
private AppConfig appConfig;
private IText text;
private WindowClosingEventHandler closing;
public AppConfig AppConfig
{
set
{
Dispatcher.Invoke(() =>
{
appConfig = value;
UpdateAppInfo();
});
}
}
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public SplashScreen(IText text, AppConfig appConfig = null)
{
this.appConfig = appConfig;
this.text = text;
InitializeComponent();
InitializeSplashScreen();
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public new void Close()
{
Dispatcher.Invoke(() =>
{
allowClose = true;
base.Close();
});
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
public void Progress()
{
model.CurrentProgress += 1;
}
public void Regress()
{
model.CurrentProgress -= 1;
}
public void SetIndeterminate()
{
model.IsIndeterminate = true;
}
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void UpdateStatus(TextKey key, bool showBusyIndication = false)
{
// TODO: Handle auto-start of busy indication
model.Status = text.Get(key);
}
private void InitializeSplashScreen()
{
UpdateAppInfo();
StatusTextBlock.DataContext = model;
ProgressBar.DataContext = model;
// To prevent the progress bar going from max to min value at startup...
model.MaxProgress = 1;
Closing += (o, args) => args.Cancel = !allowClose;
}
private void UpdateAppInfo()
{
if (appConfig != null)
{
InfoTextBlock.Inlines.Add(new Run($"Version {appConfig.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new Run(appConfig.ProgramCopyright) { FontSize = 10 });
}
}
}
}

View file

@ -1,35 +0,0 @@
<Window x:Class="SafeExamBrowser.UserInterface.Windows10.Taskbar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Windows10.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Taskbar" Height="40" Width="750" WindowStyle="None" AllowsTransparency="True" Topmost="True" Visibility="Collapsed"
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Border BorderBrush="White" BorderThickness="0,0.5,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" x:Name="ApplicationScrollViewer" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="ApplicationStackPanel" Orientation="Horizontal" />
</ScrollViewer>
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Margin="5,0,0,0" Orientation="Horizontal" VerticalAlignment="Stretch" />
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<local:DateTimeControl Grid.Column="3" />
<local:QuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid>
</Border>
</Window>

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2019 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.ComponentModel;
using System.Windows;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.UserInterface.Windows10.Utilities;
namespace SafeExamBrowser.UserInterface.Windows10
{
public partial class Taskbar : Window, ITaskbar
{
private bool allowClose;
private ILogger logger;
public event QuitButtonClickedEventHandler QuitButtonClicked;
public Taskbar(ILogger logger)
{
this.logger = logger;
InitializeComponent();
Closing += Taskbar_Closing;
Loaded += (o, args) => InitializeBounds();
QuitButtonClicked += QuitButton_Clicked;
}
public void AddApplication(IApplicationButton button)
{
if (button is UIElement uiElement)
{
ApplicationStackPanel.Children.Add(uiElement);
}
}
public void AddNotification(INotificationButton button)
{
if (button is UIElement uiElement)
{
NotificationStackPanel.Children.Add(uiElement);
}
}
public void AddSystemControl(ISystemControl control)
{
if (control is UIElement uiElement)
{
SystemControlStackPanel.Children.Add(uiElement);
}
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public int GetAbsoluteHeight()
{
return Dispatcher.Invoke(() =>
{
var height = (int) this.TransformToPhysical(Width, Height).Y;
logger.Info($"Calculated physical taskbar height is {height}px.");
return height;
});
}
public void InitializeBounds()
{
Dispatcher.Invoke(() =>
{
Width = SystemParameters.WorkArea.Right;
Left = SystemParameters.WorkArea.Right - Width;
Top = SystemParameters.WorkArea.Bottom;
var position = this.TransformToPhysical(Left, Top);
var size = this.TransformToPhysical(Width, Height);
logger.Info($"Set taskbar bounds to {Width}x{Height} at ({Left}/{Top}), in physical pixels: {size.X}x{size.Y} at ({position.X}/{position.Y}).");
});
}
private void QuitButton_Clicked(CancelEventArgs args)
{
QuitButtonClicked?.Invoke(args);
allowClose = !args.Cancel;
}
private void Taskbar_Closing(object sender, CancelEventArgs e)
{
if (!allowClose)
{
e.Cancel = true;
return;
}
foreach (var child in SystemControlStackPanel.Children)
{
if (child is ISystemControl systemControl)
{
systemControl.Close();
}
}
}
}
}

View file

@ -1,17 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Windows10.Styles">
<ControlTemplate x:Key="TaskbarButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="Center" VerticalAlignment="Center" RenderOptions.BitmapScalingMode="HighQuality" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#2AFFFFFF" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#10FFFFFF" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

View file

@ -1,137 +0,0 @@
/*
* Copyright (c) 2019 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.Threading;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.UserInterface.Windows10.Controls;
namespace SafeExamBrowser.UserInterface.Windows10
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private IText text;
public UserInterfaceFactory(IText text)
{
this.text = text;
}
public IWindow CreateAboutWindow(AppConfig appConfig)
{
return new AboutWindow(appConfig, text);
}
public IApplicationButton CreateApplicationButton(IApplicationInfo info)
{
return new ApplicationButton(info);
}
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings)
{
// TODO
// return new BrowserWindow(control, settings);
throw new System.NotImplementedException();
}
public IWindow CreateLogWindow(ILogger logger)
{
LogWindow logWindow = null;
var logWindowReadyEvent = new AutoResetEvent(false);
var logWindowThread = new Thread(() =>
{
logWindow = new LogWindow(logger, text);
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
logWindow.Show();
logWindowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
logWindowThread.SetApartmentState(ApartmentState.STA);
logWindowThread.IsBackground = true;
logWindowThread.Start();
logWindowReadyEvent.WaitOne();
return logWindow;
}
public INotificationButton CreateNotification(INotificationInfo info)
{
return new NotificationButton(info);
}
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
{
// TODO
throw new System.NotImplementedException();
}
public IPasswordDialog CreatePasswordDialog(string message, string title)
{
// TODO
throw new System.NotImplementedException();
}
public IPasswordDialog CreatePasswordDialog(TextKey message, TextKey title)
{
// TODO
throw new System.NotImplementedException();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl()
{
return new PowerSupplyControl();
}
public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)
{
// TODO
throw new System.NotImplementedException();
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
SplashScreen splashScreen = null;
var splashReadyEvent = new AutoResetEvent(false);
var splashScreenThread = new Thread(() =>
{
splashScreen = new SplashScreen(text, appConfig);
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
splashScreen.Show();
splashReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
splashScreenThread.SetApartmentState(ApartmentState.STA);
splashScreenThread.Name = nameof(SplashScreen);
splashScreenThread.IsBackground = true;
splashScreenThread.Start();
splashReadyEvent.WaitOne();
return splashScreen;
}
public ISystemWirelessNetworkControl CreateWirelessNetworkControl()
{
// TODO
throw new System.NotImplementedException();
}
}
}

View file

@ -1,59 +0,0 @@
/*
* Copyright (c) 2019 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;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.UserInterface.Windows10.Utilities
{
internal static class IconResourceLoader
{
internal static UIElement Load(IIconResource resource)
{
try
{
if (resource.IsBitmapResource)
{
return LoadBitmapResource(resource);
}
else if (resource.IsXamlResource)
{
return LoadXamlResource(resource);
}
}
catch (Exception)
{
return new TextBlock(new Run("X") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold });
}
throw new NotSupportedException($"Application icon resource of type '{resource.GetType()}' is not supported!");
}
private static UIElement LoadBitmapResource(IIconResource resource)
{
return new Image
{
Source = new BitmapImage(resource.Uri)
};
}
private static UIElement LoadXamlResource(IIconResource resource)
{
using (var stream = Application.GetResourceStream(resource.Uri)?.Stream)
{
return XamlReader.Load(stream) as UIElement;
}
}
}
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2019 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.Interop;
using System.Windows.Media;
namespace SafeExamBrowser.UserInterface.Windows10.Utilities
{
internal static class VisualExtensions
{
/// <summary>
/// WPF works with device-independent pixels. This method is required to
/// transform such values to their absolute, device-specific pixel value.
/// Source: https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels
/// </summary>
internal static Vector TransformToPhysical(this Visual visual, double x, double y)
{
Matrix transformToDevice;
var source = PresentationSource.FromVisual(visual);
if (source != null)
{
transformToDevice = source.CompositionTarget.TransformToDevice;
}
else
{
using (var newSource = new HwndSource(new HwndSourceParameters()))
{
transformToDevice = newSource.CompositionTarget.TransformToDevice;
}
}
return transformToDevice.Transform(new Vector(x, y));
}
}
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2019 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.ComponentModel;
using System.Timers;
namespace SafeExamBrowser.UserInterface.Windows10.ViewModels
{
class DateTimeViewModel : INotifyPropertyChanged
{
private Timer timer;
public string Date { get; private set; }
public string Time { get; private set; }
public string ToolTip { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public DateTimeViewModel()
{
timer = new Timer(1000);
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
var date = DateTime.Now;
Date = date.ToShortDateString();
Time = date.ToShortTimeString();
ToolTip = $"{date.ToLongDateString()} {date.ToLongTimeString()}";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Time)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Date)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ToolTip)));
}
}
}

View file

@ -1,98 +0,0 @@
/*
* Copyright (c) 2019 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;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.UserInterface.Windows10.ViewModels
{
internal class LogViewModel : ILogObserver
{
private IText text;
private ScrollViewer scrollViewer;
private TextBlock textBlock;
public string WindowTitle => text.Get(TextKey.LogWindow_Title);
public LogViewModel(IText text, ScrollViewer scrollViewer, TextBlock textBlock)
{
this.text = text;
this.scrollViewer = scrollViewer;
this.textBlock = textBlock;
}
public void Notify(ILogContent content)
{
switch (content)
{
case ILogText text:
AppendLogText(text);
break;
case ILogMessage message:
AppendLogMessage(message);
break;
default:
throw new NotImplementedException($"The log window is not yet implemented for log content of type {content.GetType()}!");
}
scrollViewer.Dispatcher.Invoke(scrollViewer.ScrollToEnd);
}
private void AppendLogText(ILogText logText)
{
textBlock.Dispatcher.Invoke(() =>
{
var isHeader = logText.Text.StartsWith("/* ");
var isComment = logText.Text.StartsWith("# ");
var brush = isHeader || isComment ? Brushes.ForestGreen : textBlock.Foreground;
textBlock.Inlines.Add(new Run($"{logText.Text}{Environment.NewLine}")
{
FontWeight = isHeader ? FontWeights.Bold : FontWeights.Normal,
Foreground = brush
});
});
}
private void AppendLogMessage(ILogMessage message)
{
textBlock.Dispatcher.Invoke(() =>
{
var date = message.DateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
var severity = message.Severity.ToString().ToUpper();
var threadInfo = $"{message.ThreadInfo.Id}{(message.ThreadInfo.HasName ? ": " + message.ThreadInfo.Name : string.Empty)}";
var infoRun = new Run($"{date} [{threadInfo}] - ") { Foreground = Brushes.Gray };
var messageRun = new Run($"{severity}: {message.Message}{Environment.NewLine}") { Foreground = GetBrushFor(message.Severity) };
textBlock.Inlines.Add(infoRun);
textBlock.Inlines.Add(messageRun);
});
}
private Brush GetBrushFor(LogLevel severity)
{
switch (severity)
{
case LogLevel.Debug:
return Brushes.Gray;
case LogLevel.Error:
return Brushes.Red;
case LogLevel.Warning:
return Brushes.Yellow;
default:
return Brushes.White;
}
}
}
}

View file

@ -1,112 +0,0 @@
/*
* Copyright (c) 2019 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.ComponentModel;
using System.Timers;
namespace SafeExamBrowser.UserInterface.Windows10.ViewModels
{
class SplashScreenViewModel : INotifyPropertyChanged
{
private int currentProgress;
private bool isIndeterminate;
private int maxProgress;
private string status;
private Timer busyTimer;
public event PropertyChangedEventHandler PropertyChanged;
public int CurrentProgress
{
get
{
return currentProgress;
}
set
{
currentProgress = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentProgress)));
}
}
public bool IsIndeterminate
{
get
{
return isIndeterminate;
}
set
{
isIndeterminate = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIndeterminate)));
}
}
public int MaxProgress
{
get
{
return maxProgress;
}
set
{
maxProgress = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MaxProgress)));
}
}
public string Status
{
get
{
return status;
}
set
{
status = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
}
}
public void StartBusyIndication()
{
StopBusyIndication();
busyTimer = new Timer
{
AutoReset = true,
Interval = 750
};
busyTimer.Elapsed += BusyTimer_Elapsed;
busyTimer.Start();
}
public void StopBusyIndication()
{
busyTimer?.Stop();
busyTimer?.Close();
}
private void BusyTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var next = Status ?? string.Empty;
if (next.EndsWith("..."))
{
next = Status.Substring(0, Status.Length - 3);
}
else
{
next += ".";
}
Status = next;
}
}
}

View file

@ -13,8 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
LICENSE.txt = LICENSE.txt
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.UserInterface.Windows10", "SafeExamBrowser.UserInterface.Windows10\SafeExamBrowser.UserInterface.Windows10.csproj", "{E1BE031A-4354-41E7-83E8-843DED4489FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Core.UnitTests", "SafeExamBrowser.Core.UnitTests\SafeExamBrowser.Core.UnitTests.csproj", "{48B9F2A1-B87D-40F0-BEC9-399E8909860F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Contracts", "SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj", "{47DA5933-BEF8-4729-94E6-ABDE2DB12262}"
@ -80,14 +78,6 @@ Global
{3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC}.Release|Any CPU.Build.0 = Release|Any CPU
{3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC}.Release|x86.ActiveCfg = Release|x86
{3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC}.Release|x86.Build.0 = Release|x86
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Debug|x86.ActiveCfg = Debug|x86
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Debug|x86.Build.0 = Debug|x86
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Release|Any CPU.Build.0 = Release|Any CPU
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Release|x86.ActiveCfg = Release|x86
{E1BE031A-4354-41E7-83E8-843DED4489FF}.Release|x86.Build.0 = Release|x86
{48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Debug|x86.ActiveCfg = Debug|x86