diff --git a/SafeExamBrowser.Browser/BrowserApplicationController.cs b/SafeExamBrowser.Browser/BrowserApplicationController.cs
new file mode 100644
index 00000000..8556c8b9
--- /dev/null
+++ b/SafeExamBrowser.Browser/BrowserApplicationController.cs
@@ -0,0 +1,30 @@
+/*
+ * 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 SafeExamBrowser.Contracts.Behaviour;
+using SafeExamBrowser.Contracts.UserInterface;
+
+namespace SafeExamBrowser.Browser
+{
+ public class BrowserApplicationController : IApplicationController
+ {
+ private IApplicationButton button;
+
+ public void RegisterApplicationButton(IApplicationButton button)
+ {
+ this.button = button;
+ this.button.OnClick += ButtonClick;
+ }
+
+ private void ButtonClick(Guid? instanceId = null)
+ {
+ button.RegisterInstance(new BrowserApplicationInstance("A new instance. Yaji..."));
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
new file mode 100644
index 00000000..fda9df52
--- /dev/null
+++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
@@ -0,0 +1,25 @@
+/*
+ * 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 SafeExamBrowser.Contracts.Configuration;
+
+namespace SafeExamBrowser.Browser
+{
+ public class BrowserApplicationInstance : IApplicationInstance
+ {
+ public Guid Id { get; private set; }
+ public string Name { get; private set; }
+
+ public BrowserApplicationInstance(string name)
+ {
+ Id = Guid.NewGuid();
+ Name = name;
+ }
+ }
+}
diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
index 31048534..c1edbf8f 100644
--- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
+++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
@@ -40,7 +40,9 @@
+
+
diff --git a/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs b/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs
index b3f04f7c..b57dd74a 100644
--- a/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs
+++ b/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs
@@ -13,8 +13,8 @@ namespace SafeExamBrowser.Contracts.Behaviour
public interface IApplicationController
{
///
- /// The handler to be executed when an application's taskbar button gets clicked.
+ /// Registers the taskbar button for this application.
///
- TaskbarButtonClickHandler OnClick { get; }
+ void RegisterApplicationButton(IApplicationButton button);
}
}
diff --git a/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs b/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs
new file mode 100644
index 00000000..651a3bdd
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs
@@ -0,0 +1,25 @@
+/*
+ * 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.Configuration
+{
+ public interface IApplicationInstance
+ {
+ ///
+ /// The unique identifier for the application instance.
+ ///
+ Guid Id { get; }
+
+ ///
+ /// The name or (document) title of the application instance.
+ ///
+ string Name { get; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Configuration/ISettings.cs b/SafeExamBrowser.Contracts/Configuration/ISettings.cs
index 6a49de0b..0b618c5b 100644
--- a/SafeExamBrowser.Contracts/Configuration/ISettings.cs
+++ b/SafeExamBrowser.Contracts/Configuration/ISettings.cs
@@ -10,23 +10,23 @@ namespace SafeExamBrowser.Contracts.Configuration
{
public interface ISettings
{
- ///
- /// The copyright information for the application, to be displayed in e.g. the log or the splash screen.
- ///
- string CopyrightInfo { get; }
-
///
/// The path where the log files are to be stored.
///
string LogFolderPath { get; }
///
- /// The information to be printed at the beginning of the application log.
+ /// The copyright information for the application (i.e. the executing assembly).
///
- string LogHeader { get; }
+ string ProgramCopyright { get; }
///
- /// The program version of the application.
+ /// The program title of the application (i.e. the executing assembly).
+ ///
+ string ProgramTitle { get; }
+
+ ///
+ /// The program version of the application (i.e. the executing assembly).
///
string ProgramVersion { get; }
}
diff --git a/SafeExamBrowser.Contracts/I18n/Key.cs b/SafeExamBrowser.Contracts/I18n/Key.cs
index 046515a8..3ef9f78c 100644
--- a/SafeExamBrowser.Contracts/I18n/Key.cs
+++ b/SafeExamBrowser.Contracts/I18n/Key.cs
@@ -18,6 +18,9 @@ namespace SafeExamBrowser.Contracts.I18n
MessageBox_SingleInstance,
MessageBox_SingleInstanceTitle,
MessageBox_StartupError,
- MessageBox_StartupErrorTitle
+ MessageBox_StartupErrorTitle,
+ SplashScreen_InitializeBrowser,
+ SplashScreen_StartupProcedure,
+ Version
}
}
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index 1e4bce48..61eeb7a4 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -43,6 +43,7 @@
+
@@ -60,7 +61,7 @@
-
+
diff --git a/SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs b/SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs
similarity index 77%
rename from SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs
rename to SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs
index 5e4859d4..c79403b8 100644
--- a/SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs
@@ -7,12 +7,13 @@
*/
using System;
+using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.Contracts.UserInterface
{
public delegate void TaskbarButtonClickHandler(Guid? instanceId = null);
- public interface ITaskbarButton
+ public interface IApplicationButton
{
///
/// OnClick handler, executed when the user clicks on the application button. If multiple instances of
@@ -21,11 +22,10 @@ namespace SafeExamBrowser.Contracts.UserInterface
event TaskbarButtonClickHandler OnClick;
///
- /// Registers a new instance of an application, to be displayed when the user clicks the taskbar button.
+ /// Registers a new instance of an application, to be displayed if the user clicks the taskbar button
+ /// when there are already one or more instances of the same application running.
///
- /// The identifier for the application instance.
- /// An optional title to be displayed (if multiple instances are active).
- void RegisterInstance(Guid id, string title = null);
+ void RegisterInstance(IApplicationInstance instance);
///
/// Unregisters an application instance, e.g. if it gets closed.
diff --git a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
index c6696ec7..8f72e6a8 100644
--- a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
@@ -6,11 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using SafeExamBrowser.Contracts.Logging;
+using SafeExamBrowser.Contracts.I18n;
namespace SafeExamBrowser.Contracts.UserInterface
{
- public interface ISplashScreen : ILogObserver
+ public interface ISplashScreen
{
///
/// Closes the splash screen.
@@ -31,5 +31,10 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// Updates the progress bar of the splash screen according to the specified amount.
///
void UpdateProgress(int amount = 1);
+
+ ///
+ /// Updates the status text of the splash screen.
+ ///
+ void UpdateText(Key key);
}
}
diff --git a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
index 709a0883..dc79083a 100644
--- a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
@@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.UserInterface
///
/// Adds the given application button to the taskbar.
///
- void AddButton(ITaskbarButton button);
+ void AddButton(IApplicationButton button);
///
/// Moves the taskbar to the given location on the screen.
diff --git a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs
index f397772f..6931871f 100644
--- a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs
@@ -15,6 +15,6 @@ namespace SafeExamBrowser.Contracts.UserInterface
///
/// Creates a taskbar button, initialized with the given application information.
///
- ITaskbarButton CreateButton(IApplicationInfo info);
+ IApplicationButton CreateApplicationButton(IApplicationInfo info);
}
}
diff --git a/SafeExamBrowser.Core/Behaviour/StartupController.cs b/SafeExamBrowser.Core/Behaviour/StartupController.cs
index 58ee5534..f25bfaab 100644
--- a/SafeExamBrowser.Core/Behaviour/StartupController.cs
+++ b/SafeExamBrowser.Core/Behaviour/StartupController.cs
@@ -20,6 +20,7 @@ namespace SafeExamBrowser.Core.Behaviour
{
public class StartupController : IStartupController
{
+ private IApplicationController browserController;
private IApplicationInfo browserInfo;
private ILogger logger;
private IMessageBox messageBox;
@@ -33,7 +34,6 @@ namespace SafeExamBrowser.Core.Behaviour
{
get
{
- yield return InitializeApplicationLog;
yield return HandleCommandLineArguments;
yield return DetectOperatingSystem;
yield return EstablishWcfServiceConnection;
@@ -46,26 +46,38 @@ namespace SafeExamBrowser.Core.Behaviour
}
}
- public StartupController(IApplicationInfo browserInfo, ILogger logger, IMessageBox messageBox, ISettings settings, ISplashScreen splashScreen, ITaskbar taskbar, IText text, IUiElementFactory uiFactory)
+ public StartupController(
+ IApplicationController browserController,
+ IApplicationInfo browserInfo,
+ ILogger logger,
+ IMessageBox messageBox,
+ ISettings settings,
+ ISplashScreen splashScreen,
+ ITaskbar taskbar,
+ IText text,
+ IUiElementFactory uiFactory)
{
- this.browserInfo = browserInfo;
- this.logger = logger;
- this.messageBox = messageBox;
- this.settings = settings;
- this.splashScreen = splashScreen;
- this.taskbar = taskbar;
- this.text = text;
- this.uiFactory = uiFactory;
+ this.browserController = browserController ?? throw new ArgumentNullException(nameof(browserController));
+ this.browserInfo = browserInfo ?? throw new ArgumentNullException(nameof(browserInfo));
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); ;
+ this.messageBox = messageBox ?? throw new ArgumentNullException(nameof(messageBox)); ;
+ this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); ;
+ this.splashScreen = splashScreen ?? throw new ArgumentNullException(nameof(splashScreen)); ;
+ this.taskbar = taskbar ?? throw new ArgumentNullException(nameof(taskbar)); ;
+ this.text = text ?? throw new ArgumentNullException(nameof(text)); ;
+ this.uiFactory = uiFactory ?? throw new ArgumentNullException(nameof(uiFactory)); ;
}
public bool TryInitializeApplication()
{
try
{
+ InitializeApplicationLog();
+ InitializeSplashScreen();
+
foreach (var operation in StartupOperations)
{
operation();
-
splashScreen.UpdateProgress();
// TODO: Remove!
@@ -85,12 +97,20 @@ namespace SafeExamBrowser.Core.Behaviour
private void InitializeApplicationLog()
{
- logger.Log(settings.LogHeader);
+ var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}";
+ var copyrightLine = $"/* {settings.ProgramCopyright}{Environment.NewLine}";
+ var emptyLine = $"/* {Environment.NewLine}";
+ var githubLine = $"/* Please visit https://github.com/SafeExamBrowser for more information.";
+
+ logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}");
logger.Log($"{Environment.NewLine}# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}{Environment.NewLine}");
logger.Info("Initiating startup procedure.");
- logger.Subscribe(splashScreen);
+ }
+ private void InitializeSplashScreen()
+ {
splashScreen.SetMaxProgress(StartupOperations.Count());
+ splashScreen.UpdateText(Key.SplashScreen_StartupProcedure);
}
private void HandleCommandLineArguments()
@@ -151,9 +171,12 @@ namespace SafeExamBrowser.Core.Behaviour
private void InitializeBrowser()
{
- logger.Info("Initializing browser.");
+ var browserButton = uiFactory.CreateApplicationButton(browserInfo);
- var browserButton = uiFactory.CreateButton(browserInfo);
+ logger.Info("Initializing browser.");
+ splashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
+
+ browserController.RegisterApplicationButton(browserButton);
// TODO
@@ -163,7 +186,6 @@ namespace SafeExamBrowser.Core.Behaviour
private void FinishInitialization()
{
logger.Info("Application successfully initialized!");
- logger.Unsubscribe(splashScreen);
}
}
}
diff --git a/SafeExamBrowser.Core/Configuration/Settings.cs b/SafeExamBrowser.Core/Configuration/Settings.cs
index b55e8a76..56b5ae25 100644
--- a/SafeExamBrowser.Core/Configuration/Settings.cs
+++ b/SafeExamBrowser.Core/Configuration/Settings.cs
@@ -15,7 +15,15 @@ namespace SafeExamBrowser.Core.Configuration
{
public class Settings : ISettings
{
- public string CopyrightInfo
+ public string LogFolderPath
+ {
+ get
+ {
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs");
+ }
+ }
+
+ public string ProgramCopyright
{
get
{
@@ -26,28 +34,14 @@ namespace SafeExamBrowser.Core.Configuration
}
}
- public string LogFolderPath
+ public string ProgramTitle
{
get
{
- return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs");
- }
- }
-
- public string LogHeader
- {
- get
- {
- var newline = Environment.NewLine;
var executable = Assembly.GetEntryAssembly();
var title = executable.GetCustomAttribute().Title;
- var titleLine = $"/* {title}, Version {ProgramVersion}{newline}";
- var copyrightLine = $"/* {CopyrightInfo}{newline}";
- var emptyLine = $"/* {newline}";
- var githubLine = $"/* Please visit https://github.com/SafeExamBrowser for more information.";
-
- return $"{titleLine}{copyrightLine}{emptyLine}{githubLine}";
+ return title;
}
}
diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml
index bd495c9e..ec535e8f 100644
--- a/SafeExamBrowser.Core/I18n/Text.xml
+++ b/SafeExamBrowser.Core/I18n/Text.xml
@@ -4,4 +4,7 @@
Shutdown Error
An unexpected error occurred during the startup procedure! Please consult the application log for more information...
Startup Error
+ Initializing browser.
+ Initiating startup procedure.
+ Version
\ No newline at end of file
diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
new file mode 100644
index 00000000..57c6041d
--- /dev/null
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs
new file mode 100644
index 00000000..5803c6a6
--- /dev/null
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs
@@ -0,0 +1,74 @@
+/*
+ * 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.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using SafeExamBrowser.Contracts.Configuration;
+using SafeExamBrowser.Contracts.UserInterface;
+using SafeExamBrowser.UserInterface.Utilities;
+
+namespace SafeExamBrowser.UserInterface.Controls
+{
+ public partial class ApplicationButton : UserControl, IApplicationButton
+ {
+ private IApplicationInfo info;
+ private IList instances = new List();
+
+ public event TaskbarButtonClickHandler OnClick;
+
+ public ApplicationButton(IApplicationInfo info)
+ {
+ this.info = info;
+
+ InitializeComponent();
+ InitializeApplicationButton();
+ }
+
+ public void RegisterInstance(IApplicationInstance instance)
+ {
+ var instanceButton = new ApplicationInstanceButton(instance, info);
+
+ instances.Add(instance);
+ instanceButton.Click += (id) => OnClick?.Invoke(id);
+ InstanceStackPanel.Children.Add(instanceButton);
+
+ if (instances.Count > 1)
+ {
+ InstancePopup.IsOpen = true;
+ }
+ }
+
+ public void UnregisterInstance(Guid id)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void InitializeApplicationButton()
+ {
+ Button.ToolTip = info.Tooltip;
+ Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver;
+ Button.Content = ApplicationIconResourceLoader.Load(info.IconResource);
+
+ InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false;
+ }
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (instances.Count <= 1)
+ {
+ OnClick?.Invoke();
+ }
+ else
+ {
+ InstancePopup.IsOpen = true;
+ }
+ }
+ }
+}
diff --git a/SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
similarity index 92%
rename from SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml
rename to SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
index b6ee6bc7..fe5bcc08 100644
--- a/SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
@@ -1,13 +1,12 @@
-
+ mc:Ignorable="d" d:DesignWidth="250">
-