SEBWIN-308: Implemented basic file system dialog and download configuration for browser.
This commit is contained in:
parent
61f369a9a3
commit
0083a87bce
26 changed files with 881 additions and 14 deletions
|
@ -106,6 +106,7 @@ namespace SafeExamBrowser.Browser
|
||||||
private void InitializeControl()
|
private void InitializeControl()
|
||||||
{
|
{
|
||||||
var contextMenuHandler = new ContextMenuHandler();
|
var contextMenuHandler = new ContextMenuHandler();
|
||||||
|
var dialogHandler = new DialogHandler();
|
||||||
var displayHandler = new DisplayHandler();
|
var displayHandler = new DisplayHandler();
|
||||||
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
|
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
|
||||||
var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
|
var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
|
||||||
|
@ -117,6 +118,7 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
Icon = new BrowserIconResource();
|
Icon = new BrowserIconResource();
|
||||||
|
|
||||||
|
dialogHandler.DialogRequested += DialogHandler_DialogRequested;
|
||||||
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
||||||
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
||||||
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
||||||
|
@ -130,7 +132,7 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
InitializeRequestFilter(requestFilter);
|
InitializeRequestFilter(requestFilter);
|
||||||
|
|
||||||
control = new BrowserControl(contextMenuHandler, displayHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, startUrl);
|
control = new BrowserControl(contextMenuHandler, dialogHandler, displayHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, startUrl);
|
||||||
control.AddressChanged += Control_AddressChanged;
|
control.AddressChanged += Control_AddressChanged;
|
||||||
control.LoadingStateChanged += Control_LoadingStateChanged;
|
control.LoadingStateChanged += Control_LoadingStateChanged;
|
||||||
control.TitleChanged += Control_TitleChanged;
|
control.TitleChanged += Control_TitleChanged;
|
||||||
|
@ -207,6 +209,23 @@ namespace SafeExamBrowser.Browser
|
||||||
TitleChanged?.Invoke(Title);
|
TitleChanged?.Invoke(Title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
|
||||||
|
{
|
||||||
|
var dialog = uiFactory.CreateFileSystemDialog(args.Element, args.InitialPath, args.Operation, title: args.Title);
|
||||||
|
var result = dialog.Show(window);
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
args.FullPath = result.FullPath;
|
||||||
|
args.Success = result.Success;
|
||||||
|
logger.Debug($"User selected path '{result.FullPath}' when asked to {args.Operation}->{args.Element}.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Debug($"User aborted file system dialog to {args.Operation}->{args.Element}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DisplayHandler_FaviconChanged(string uri)
|
private void DisplayHandler_FaviconChanged(string uri)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestMessage(HttpMethod.Head, uri);
|
var request = new HttpRequestMessage(HttpMethod.Head, uri);
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace SafeExamBrowser.Browser
|
||||||
internal class BrowserControl : ChromiumWebBrowser, IBrowserControl
|
internal class BrowserControl : ChromiumWebBrowser, IBrowserControl
|
||||||
{
|
{
|
||||||
private IContextMenuHandler contextMenuHandler;
|
private IContextMenuHandler contextMenuHandler;
|
||||||
|
private IDialogHandler dialogHandler;
|
||||||
private IDisplayHandler displayHandler;
|
private IDisplayHandler displayHandler;
|
||||||
private IDownloadHandler downloadHandler;
|
private IDownloadHandler downloadHandler;
|
||||||
private IKeyboardHandler keyboardHandler;
|
private IKeyboardHandler keyboardHandler;
|
||||||
|
@ -49,6 +50,7 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
public BrowserControl(
|
public BrowserControl(
|
||||||
IContextMenuHandler contextMenuHandler,
|
IContextMenuHandler contextMenuHandler,
|
||||||
|
IDialogHandler dialogHandler,
|
||||||
IDisplayHandler displayHandler,
|
IDisplayHandler displayHandler,
|
||||||
IDownloadHandler downloadHandler,
|
IDownloadHandler downloadHandler,
|
||||||
IKeyboardHandler keyboardHandler,
|
IKeyboardHandler keyboardHandler,
|
||||||
|
@ -57,6 +59,7 @@ namespace SafeExamBrowser.Browser
|
||||||
string url) : base(url)
|
string url) : base(url)
|
||||||
{
|
{
|
||||||
this.contextMenuHandler = contextMenuHandler;
|
this.contextMenuHandler = contextMenuHandler;
|
||||||
|
this.dialogHandler = dialogHandler;
|
||||||
this.displayHandler = displayHandler;
|
this.displayHandler = displayHandler;
|
||||||
this.downloadHandler = downloadHandler;
|
this.downloadHandler = downloadHandler;
|
||||||
this.keyboardHandler = keyboardHandler;
|
this.keyboardHandler = keyboardHandler;
|
||||||
|
@ -70,6 +73,7 @@ namespace SafeExamBrowser.Browser
|
||||||
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
|
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
|
||||||
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
|
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
|
||||||
|
|
||||||
|
DialogHandler = dialogHandler;
|
||||||
DisplayHandler = displayHandler;
|
DisplayHandler = displayHandler;
|
||||||
DownloadHandler = downloadHandler;
|
DownloadHandler = downloadHandler;
|
||||||
KeyboardHandler = keyboardHandler;
|
KeyboardHandler = keyboardHandler;
|
||||||
|
|
22
SafeExamBrowser.Browser/Events/DialogRequestedEventArgs.cs
Normal file
22
SafeExamBrowser.Browser/Events/DialogRequestedEventArgs.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.UserInterface.Contracts.FileSystemDialog;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Browser.Events
|
||||||
|
{
|
||||||
|
internal class DialogRequestedEventArgs
|
||||||
|
{
|
||||||
|
internal FileSystemElement Element { get; set; }
|
||||||
|
internal string InitialPath { get; set; }
|
||||||
|
internal FileSystemOperation Operation { get; set; }
|
||||||
|
internal string FullPath { get; set; }
|
||||||
|
internal bool Success { get; set; }
|
||||||
|
internal string Title { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.Browser.Events
|
||||||
|
{
|
||||||
|
internal delegate void DialogRequestedEventHandler(DialogRequestedEventArgs args);
|
||||||
|
}
|
73
SafeExamBrowser.Browser/Handlers/DialogHandler.cs
Normal file
73
SafeExamBrowser.Browser/Handlers/DialogHandler.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CefSharp;
|
||||||
|
using SafeExamBrowser.Browser.Events;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
{
|
||||||
|
internal class DialogHandler : IDialogHandler
|
||||||
|
{
|
||||||
|
internal event DialogRequestedEventHandler DialogRequested;
|
||||||
|
|
||||||
|
public bool OnFileDialog(IWebBrowser webBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback)
|
||||||
|
{
|
||||||
|
var args = new DialogRequestedEventArgs
|
||||||
|
{
|
||||||
|
Element = ToElement(mode),
|
||||||
|
InitialPath = defaultFilePath,
|
||||||
|
Operation = ToOperation(mode),
|
||||||
|
Title = title
|
||||||
|
};
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
DialogRequested?.Invoke(args);
|
||||||
|
|
||||||
|
using (callback)
|
||||||
|
{
|
||||||
|
if (args.Success)
|
||||||
|
{
|
||||||
|
callback.Continue(selectedAcceptFilter, new List<string> { args.FullPath });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystemElement ToElement(CefFileDialogMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case CefFileDialogMode.OpenFolder:
|
||||||
|
return FileSystemElement.Folder;
|
||||||
|
default:
|
||||||
|
return FileSystemElement.File;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystemOperation ToOperation(CefFileDialogMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case CefFileDialogMode.Save:
|
||||||
|
return FileSystemOperation.Save;
|
||||||
|
default:
|
||||||
|
return FileSystemOperation.Open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ using CefSharp;
|
||||||
using SafeExamBrowser.Browser.Contracts.Events;
|
using SafeExamBrowser.Browser.Contracts.Events;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using Syroot.Windows.IO;
|
||||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Browser.Handlers
|
namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
@ -25,9 +26,9 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
|
|
||||||
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
||||||
|
|
||||||
public DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
|
internal DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
|
||||||
{
|
{
|
||||||
this.appConfig = appConfig;
|
this.appConfig = appConfig;
|
||||||
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
||||||
|
@ -41,7 +42,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
var extension = Path.GetExtension(uri.AbsolutePath);
|
var extension = Path.GetExtension(uri.AbsolutePath);
|
||||||
var isConfigFile = String.Equals(extension, appConfig.ConfigurationFileExtension, StringComparison.OrdinalIgnoreCase);
|
var isConfigFile = String.Equals(extension, appConfig.ConfigurationFileExtension, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
logger.Debug($"Handling download request for '{uri}'.");
|
logger.Debug($"Detected download request for '{uri}'.");
|
||||||
|
|
||||||
if (isConfigFile)
|
if (isConfigFile)
|
||||||
{
|
{
|
||||||
|
@ -49,12 +50,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
}
|
}
|
||||||
else if (settings.AllowDownloads)
|
else if (settings.AllowDownloads)
|
||||||
{
|
{
|
||||||
logger.Debug($"Starting download of '{uri}'...");
|
Task.Run(() => HandleFileDownload(downloadItem, callback));
|
||||||
|
|
||||||
using (callback)
|
|
||||||
{
|
|
||||||
callback.Continue(null, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -64,6 +60,8 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
|
||||||
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
|
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
|
||||||
{
|
{
|
||||||
|
// TODO: Show download progress in respective window -> event for BrowserApplicationInstance!
|
||||||
|
|
||||||
if (downloadItem.IsComplete || downloadItem.IsCancelled)
|
if (downloadItem.IsComplete || downloadItem.IsCancelled)
|
||||||
{
|
{
|
||||||
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
||||||
|
@ -72,6 +70,39 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
||||||
|
|
||||||
|
// TODO: Show success message or download icon like Firefox in respective window!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleFileDownload(DownloadItem downloadItem, IBeforeDownloadCallback callback)
|
||||||
|
{
|
||||||
|
var filePath = default(string);
|
||||||
|
var showDialog = settings.AllowCustomDownloadLocation;
|
||||||
|
|
||||||
|
logger.Debug($"Handling download of file '{downloadItem.SuggestedFileName}'.");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(settings.DownloadDirectory))
|
||||||
|
{
|
||||||
|
filePath = Path.Combine(Environment.ExpandEnvironmentVariables(settings.DownloadDirectory), downloadItem.SuggestedFileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filePath = Path.Combine(KnownFolders.Downloads.ExpandedPath, downloadItem.SuggestedFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showDialog)
|
||||||
|
{
|
||||||
|
logger.Debug($"Allowing user to select custom download location, with '{filePath}' as suggestion.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Debug($"Automatically downloading file as '{filePath}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (callback)
|
||||||
|
{
|
||||||
|
callback.Continue(filePath, showDialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +110,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
{
|
{
|
||||||
var args = new DownloadEventArgs();
|
var args = new DownloadEventArgs();
|
||||||
|
|
||||||
logger.Debug($"Detected download request for configuration file '{downloadItem.Url}'.");
|
logger.Debug($"Handling download of configuration file '{downloadItem.SuggestedFileName}'.");
|
||||||
ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
|
ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
|
||||||
|
|
||||||
if (args.AllowDownload)
|
if (args.AllowDownload)
|
||||||
|
@ -89,7 +120,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
callbacks[downloadItem.Id] = args.Callback;
|
callbacks[downloadItem.Id] = args.Callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug($"Starting download of configuration file '{downloadItem.Url}'...");
|
logger.Debug($"Starting download of configuration file '{downloadItem.SuggestedFileName}'...");
|
||||||
|
|
||||||
using (callback)
|
using (callback)
|
||||||
{
|
{
|
||||||
|
@ -98,7 +129,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Debug($"Download of configuration file '{downloadItem.Url}' was cancelled.");
|
logger.Debug($"Download of configuration file '{downloadItem.SuggestedFileName}' was cancelled.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,9 @@
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Syroot.KnownFolders, Version=1.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Syroot.Windows.IO.KnownFolders.1.2.1\lib\net452\Syroot.KnownFolders.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
|
@ -63,6 +66,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BrowserApplication.cs" />
|
<Compile Include="BrowserApplication.cs" />
|
||||||
<Compile Include="BrowserApplicationInstance.cs" />
|
<Compile Include="BrowserApplicationInstance.cs" />
|
||||||
|
<Compile Include="Events\DialogRequestedEventArgs.cs" />
|
||||||
|
<Compile Include="Events\DialogRequestedEventHandler.cs" />
|
||||||
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
||||||
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
||||||
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
||||||
|
@ -78,6 +83,7 @@
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="BrowserIconResource.cs" />
|
<Compile Include="BrowserIconResource.cs" />
|
||||||
|
<Compile Include="Handlers\DialogHandler.cs" />
|
||||||
<Compile Include="Handlers\DisplayHandler.cs" />
|
<Compile Include="Handlers\DisplayHandler.cs" />
|
||||||
<Compile Include="Handlers\DownloadHandler.cs" />
|
<Compile Include="Handlers\DownloadHandler.cs" />
|
||||||
<Compile Include="Handlers\KeyboardHandler.cs" />
|
<Compile Include="Handlers\KeyboardHandler.cs" />
|
||||||
|
|
|
@ -4,4 +4,5 @@
|
||||||
<package id="cef.redist.x86" version="75.1.14" targetFramework="net472" />
|
<package id="cef.redist.x86" version="75.1.14" targetFramework="net472" />
|
||||||
<package id="CefSharp.Common" version="75.1.142" targetFramework="net472" />
|
<package id="CefSharp.Common" version="75.1.142" targetFramework="net472" />
|
||||||
<package id="CefSharp.WinForms" version="75.1.142" targetFramework="net472" />
|
<package id="CefSharp.WinForms" version="75.1.142" targetFramework="net472" />
|
||||||
|
<package id="Syroot.Windows.IO.KnownFolders" version="1.2.1" targetFramework="net472" />
|
||||||
</packages>
|
</packages>
|
|
@ -24,6 +24,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
case Keys.Browser.AllowConfigurationDownloads:
|
case Keys.Browser.AllowConfigurationDownloads:
|
||||||
MapAllowConfigurationDownloads(settings, value);
|
MapAllowConfigurationDownloads(settings, value);
|
||||||
break;
|
break;
|
||||||
|
case Keys.Browser.AllowCustomDownloadLocation:
|
||||||
|
MapAllowCustomDownloadLocation(settings, value);
|
||||||
|
break;
|
||||||
case Keys.Browser.AllowDeveloperConsole:
|
case Keys.Browser.AllowDeveloperConsole:
|
||||||
MapAllowDeveloperConsole(settings, value);
|
MapAllowDeveloperConsole(settings, value);
|
||||||
break;
|
break;
|
||||||
|
@ -54,6 +57,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
case Keys.Browser.AdditionalWindow.WindowWidth:
|
case Keys.Browser.AdditionalWindow.WindowWidth:
|
||||||
MapWindowWidthAdditionalWindow(settings, value);
|
MapWindowWidthAdditionalWindow(settings, value);
|
||||||
break;
|
break;
|
||||||
|
case Keys.Browser.DownloadDirectory:
|
||||||
|
MapDownloadDirectory(settings, value);
|
||||||
|
break;
|
||||||
case Keys.Browser.EnableBrowser:
|
case Keys.Browser.EnableBrowser:
|
||||||
MapEnableBrowser(settings, value);
|
MapEnableBrowser(settings, value);
|
||||||
break;
|
break;
|
||||||
|
@ -139,6 +145,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MapAllowCustomDownloadLocation(AppSettings settings, object value)
|
||||||
|
{
|
||||||
|
if (value is bool allow)
|
||||||
|
{
|
||||||
|
settings.Browser.AllowCustomDownloadLocation = allow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void MapAllowDeveloperConsole(AppSettings settings, object value)
|
private void MapAllowDeveloperConsole(AppSettings settings, object value)
|
||||||
{
|
{
|
||||||
if (value is bool allow)
|
if (value is bool allow)
|
||||||
|
@ -198,6 +212,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MapDownloadDirectory(AppSettings settings, object value)
|
||||||
|
{
|
||||||
|
if (value is string directory)
|
||||||
|
{
|
||||||
|
settings.Browser.DownloadDirectory = directory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void MapEnableBrowser(AppSettings settings, object value)
|
private void MapEnableBrowser(AppSettings settings, object value)
|
||||||
{
|
{
|
||||||
if (value is bool enable)
|
if (value is bool enable)
|
||||||
|
|
|
@ -42,11 +42,13 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
internal static class Browser
|
internal static class Browser
|
||||||
{
|
{
|
||||||
internal const string AllowConfigurationDownloads = "downloadAndOpenSebConfig";
|
internal const string AllowConfigurationDownloads = "downloadAndOpenSebConfig";
|
||||||
|
internal const string AllowCustomDownloadLocation = "allowCustomDownloadLocation";
|
||||||
internal const string AllowDeveloperConsole = "allowDeveloperConsole";
|
internal const string AllowDeveloperConsole = "allowDeveloperConsole";
|
||||||
internal const string AllowDownloads = "allowDownUploads";
|
internal const string AllowDownloads = "allowDownUploads";
|
||||||
internal const string AllowPageZoom = "enableZoomPage";
|
internal const string AllowPageZoom = "enableZoomPage";
|
||||||
internal const string CustomUserAgentDesktop = "browserUserAgentWinDesktopModeCustom";
|
internal const string CustomUserAgentDesktop = "browserUserAgentWinDesktopModeCustom";
|
||||||
internal const string CustomUserAgentMobile = "browserUserAgentWinTouchModeCustom";
|
internal const string CustomUserAgentMobile = "browserUserAgentWinTouchModeCustom";
|
||||||
|
internal const string DownloadDirectory = "downloadDirectoryWin";
|
||||||
internal const string EnableBrowser = "enableSebBrowser";
|
internal const string EnableBrowser = "enableSebBrowser";
|
||||||
internal const string PopupPolicy = "newBrowserWindowByLinkPolicy";
|
internal const string PopupPolicy = "newBrowserWindowByLinkPolicy";
|
||||||
internal const string PopupBlockForeignHost = "newBrowserWindowByLinkBlockForeign";
|
internal const string PopupBlockForeignHost = "newBrowserWindowByLinkBlockForeign";
|
||||||
|
|
|
@ -23,6 +23,18 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
BrowserWindow_DeveloperConsoleMenuItem,
|
BrowserWindow_DeveloperConsoleMenuItem,
|
||||||
BrowserWindow_ZoomMenuItem,
|
BrowserWindow_ZoomMenuItem,
|
||||||
Build,
|
Build,
|
||||||
|
FileSystemDialog_Cancel,
|
||||||
|
FileSystemDialog_LoadError,
|
||||||
|
FileSystemDialog_Loading,
|
||||||
|
FileSystemDialog_OpenFileMessage,
|
||||||
|
FileSystemDialog_OpenFolderMessage,
|
||||||
|
FileSystemDialog_OverwriteWarning,
|
||||||
|
FileSystemDialog_OverwriteWarningTitle,
|
||||||
|
FileSystemDialog_SaveAs,
|
||||||
|
FileSystemDialog_SaveFileMessage,
|
||||||
|
FileSystemDialog_SaveFolderMessage,
|
||||||
|
FileSystemDialog_Select,
|
||||||
|
FileSystemDialog_Title,
|
||||||
FolderDialog_ApplicationLocation,
|
FolderDialog_ApplicationLocation,
|
||||||
LockScreen_AllowOption,
|
LockScreen_AllowOption,
|
||||||
LockScreen_Message,
|
LockScreen_Message,
|
||||||
|
|
|
@ -27,6 +27,42 @@
|
||||||
<Entry key="Build">
|
<Entry key="Build">
|
||||||
Build
|
Build
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_Cancel">
|
||||||
|
Cancel
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_LoadError">
|
||||||
|
Failed to load data!
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_Loading">
|
||||||
|
Loading...
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_OpenFileMessage">
|
||||||
|
Please select a file to open.
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_OpenFolderMessage">
|
||||||
|
Please select a folder.
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_OverwriteWarning">
|
||||||
|
The selected file already exists! Would you really like to overwrite it?
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_OverwriteWarningTitle">
|
||||||
|
Overwrite?
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_SaveAs">
|
||||||
|
Save as:
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_SaveFileMessage">
|
||||||
|
Please select a location to save the file.
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_SaveFolderMessage">
|
||||||
|
Please select a location to save the folder.
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_Select">
|
||||||
|
Select
|
||||||
|
</Entry>
|
||||||
|
<Entry key="FileSystemDialog_Title">
|
||||||
|
File System Access
|
||||||
|
</Entry>
|
||||||
<Entry key="FolderDialog_ApplicationLocation">
|
<Entry key="FolderDialog_ApplicationLocation">
|
||||||
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
|
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
|
||||||
</Entry>
|
</Entry>
|
||||||
|
|
|
@ -26,6 +26,11 @@ namespace SafeExamBrowser.Settings.Browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowConfigurationDownloads { get; set; }
|
public bool AllowConfigurationDownloads { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the user will be allowed to select a custom location when downloading a file (excluding configuration files).
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowCustomDownloadLocation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the user will be allowed to download files (excluding configuration files).
|
/// Determines whether the user will be allowed to download files (excluding configuration files).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -46,6 +51,11 @@ namespace SafeExamBrowser.Settings.Browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CustomUserAgent { get; set; }
|
public string CustomUserAgent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a custom directory for file downloads. If not defined, all downloads will be saved in the current user's download directory.
|
||||||
|
/// </summary>
|
||||||
|
public string DownloadDirectory { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the user is allowed to use the integrated browser application.
|
/// Determines whether the user is allowed to use the integrated browser application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.UserInterface.Contracts.FileSystemDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the user interaction result of an <see cref="IFileSystemDialog"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class FileSystemDialogResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The full path of the item selected by the user, or <c>null</c> if the interaction was unsuccessful.
|
||||||
|
/// </summary>
|
||||||
|
public string FullPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the user confirmed the dialog or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.UserInterface.Contracts.FileSystemDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines all elements supported by an <see cref="IFileSystemDialog"/>
|
||||||
|
/// </summary>
|
||||||
|
public enum FileSystemElement
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dialog to perform an operation with a file.
|
||||||
|
/// </summary>
|
||||||
|
File,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dialog to perform an operation with a folder.
|
||||||
|
/// </summary>
|
||||||
|
Folder
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.UserInterface.Contracts.FileSystemDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines all operations supported by an <see cref="IFileSystemDialog"/>
|
||||||
|
/// </summary>
|
||||||
|
public enum FileSystemOperation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dialog to open a file system element.
|
||||||
|
/// </summary>
|
||||||
|
Open,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dialog to save a file system element.
|
||||||
|
/// </summary>
|
||||||
|
Save
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.UserInterface.Contracts.Windows;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Contracts.FileSystemDialog
|
||||||
|
{
|
||||||
|
public interface IFileSystemDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the dialog to the user. If a parent window is specified, the dialog is rendered modally for the given parent.
|
||||||
|
/// </summary>
|
||||||
|
FileSystemDialogResult Show(IWindow parent = null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -49,6 +50,11 @@ namespace SafeExamBrowser.UserInterface.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
|
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a file system dialog according to the given parameters.
|
||||||
|
/// </summary>
|
||||||
|
IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a folder dialog with the given message.
|
/// Creates a folder dialog with the given message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -60,6 +60,10 @@
|
||||||
<Compile Include="Browser\IBrowserControl.cs" />
|
<Compile Include="Browser\IBrowserControl.cs" />
|
||||||
<Compile Include="Browser\IBrowserWindow.cs" />
|
<Compile Include="Browser\IBrowserWindow.cs" />
|
||||||
<Compile Include="Events\ActionRequestedEventHandler.cs" />
|
<Compile Include="Events\ActionRequestedEventHandler.cs" />
|
||||||
|
<Compile Include="FileSystemDialog\FileSystemDialogResult.cs" />
|
||||||
|
<Compile Include="FileSystemDialog\FileSystemElement.cs" />
|
||||||
|
<Compile Include="FileSystemDialog\FileSystemOperation.cs" />
|
||||||
|
<Compile Include="FileSystemDialog\IFileSystemDialog.cs" />
|
||||||
<Compile Include="IProgressIndicator.cs" />
|
<Compile Include="IProgressIndicator.cs" />
|
||||||
<Compile Include="IUserInterfaceFactory.cs" />
|
<Compile Include="IUserInterfaceFactory.cs" />
|
||||||
<Compile Include="MessageBox\IMessageBox.cs" />
|
<Compile Include="MessageBox\IMessageBox.cs" />
|
||||||
|
|
45
SafeExamBrowser.UserInterface.Desktop/FileSystemDialog.xaml
Normal file
45
SafeExamBrowser.UserInterface.Desktop/FileSystemDialog.xaml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<Window x:Class="SafeExamBrowser.UserInterface.Desktop.FileSystemDialog"
|
||||||
|
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:fa="http://schemas.fontawesome.io/icons/"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
||||||
|
mc:Ignorable="d" Height="500" Width="750" ResizeMode="NoResize">
|
||||||
|
<Window.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="./Templates/Colors.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Window.Resources>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<WrapPanel Grid.Row="0" Margin="20,10">
|
||||||
|
<fa:ImageAwesome Name="OperationIcon" Foreground="LightGray" Height="25" Icon="FileOutline"/>
|
||||||
|
<TextBlock Name="Message" Margin="10,0,0,0" VerticalAlignment="Center" />
|
||||||
|
</WrapPanel>
|
||||||
|
<TreeView Grid.Row="1" Name="FileSystem" Margin="10,0" TreeViewItem.Expanded="FileSystem_Expanded" />
|
||||||
|
<TextBlock Grid.Row="2" Name="SelectedElement" Margin="10,5,10,5" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
|
||||||
|
<Grid Grid.Row="3" Name="NewElement" Margin="10,0,10,10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Name="NewElementLabel" VerticalAlignment="Center"/>
|
||||||
|
<TextBox Grid.Column="1" Name="NewElementName" Height="25" Margin="5,0,0,0" VerticalContentAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="4" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<WrapPanel Orientation="Horizontal" Margin="20" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||||
|
<Button Name="SelectButton" Cursor="Hand" IsEnabled="False" Margin="10,0" Padding="10,5" MinWidth="75" />
|
||||||
|
<Button Name="CancelButton" Cursor="Hand" Padding="20,5" MinWidth="75" />
|
||||||
|
</WrapPanel>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
382
SafeExamBrowser.UserInterface.Desktop/FileSystemDialog.xaml.cs
Normal file
382
SafeExamBrowser.UserInterface.Desktop/FileSystemDialog.xaml.cs
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using FontAwesome.WPF;
|
||||||
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
{
|
||||||
|
public partial class FileSystemDialog : Window, IFileSystemDialog
|
||||||
|
{
|
||||||
|
private FileSystemElement element;
|
||||||
|
private string initialPath;
|
||||||
|
private string message;
|
||||||
|
private FileSystemOperation operation;
|
||||||
|
private IText text;
|
||||||
|
private string title;
|
||||||
|
|
||||||
|
public FileSystemDialog(
|
||||||
|
FileSystemElement element,
|
||||||
|
string initialPath,
|
||||||
|
FileSystemOperation operation,
|
||||||
|
IText text,
|
||||||
|
string message = default(string),
|
||||||
|
string title = default(string))
|
||||||
|
{
|
||||||
|
this.element = element;
|
||||||
|
this.initialPath = initialPath;
|
||||||
|
this.message = message;
|
||||||
|
this.operation = operation;
|
||||||
|
this.text = text;
|
||||||
|
this.title = title;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
InitializeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSystemDialogResult Show(IWindow parent = null)
|
||||||
|
{
|
||||||
|
return Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
var result = new FileSystemDialogResult();
|
||||||
|
|
||||||
|
if (parent is Window)
|
||||||
|
{
|
||||||
|
Owner = parent as Window;
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShowDialog() == true)
|
||||||
|
{
|
||||||
|
result.FullPath = BuildFullPath();
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FileSystem_Expanded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Source is TreeViewItem item && item.Items.Count == 1 && !(item.Items[0] is TreeViewItem))
|
||||||
|
{
|
||||||
|
Load(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FileSystem_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||||
|
{
|
||||||
|
if (e.NewValue is TreeViewItem item)
|
||||||
|
{
|
||||||
|
if (item.Tag is DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
SelectButton.IsEnabled = element == FileSystemElement.Folder || operation == FileSystemOperation.Save;
|
||||||
|
SelectedElement.Text = directory.FullName;
|
||||||
|
SelectedElement.ToolTip = directory.FullName;
|
||||||
|
}
|
||||||
|
else if (item.Tag is FileInfo file)
|
||||||
|
{
|
||||||
|
SelectButton.IsEnabled = element == FileSystemElement.File;
|
||||||
|
SelectedElement.Text = file.FullName;
|
||||||
|
SelectedElement.ToolTip = file.FullName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectButton.IsEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewElementName_KeyUp(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter && IsValid())
|
||||||
|
{
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (IsValid())
|
||||||
|
{
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildFullPath()
|
||||||
|
{
|
||||||
|
var fullPath = SelectedElement.Text;
|
||||||
|
|
||||||
|
if (operation == FileSystemOperation.Save)
|
||||||
|
{
|
||||||
|
fullPath = Path.Combine(SelectedElement.Text, NewElementName.Text);
|
||||||
|
|
||||||
|
if (element == FileSystemElement.File)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(initialPath);
|
||||||
|
|
||||||
|
if (!fullPath.EndsWith(extension))
|
||||||
|
{
|
||||||
|
fullPath = $"{fullPath}{extension}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValid()
|
||||||
|
{
|
||||||
|
var fullPath = BuildFullPath();
|
||||||
|
var isValid = true;
|
||||||
|
|
||||||
|
if (element == FileSystemElement.File && operation == FileSystemOperation.Save && File.Exists(fullPath))
|
||||||
|
{
|
||||||
|
var message = text.Get(TextKey.FileSystemDialog_OverwriteWarning);
|
||||||
|
var title = text.Get(TextKey.FileSystemDialog_OverwriteWarningTitle);
|
||||||
|
var result = System.Windows.MessageBox.Show(this, message, title, MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||||
|
|
||||||
|
isValid = result == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Load(TreeViewItem item)
|
||||||
|
{
|
||||||
|
item.Items.Clear();
|
||||||
|
|
||||||
|
if (item.Tag is DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
FileSystem.Cursor = Cursors.Wait;
|
||||||
|
item.BeginInit();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var subDirectory in directory.GetDirectories())
|
||||||
|
{
|
||||||
|
if (!subDirectory.Attributes.HasFlag(FileAttributes.Hidden))
|
||||||
|
{
|
||||||
|
item.Items.Add(CreateItem(subDirectory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in directory.GetFiles())
|
||||||
|
{
|
||||||
|
if (!file.Attributes.HasFlag(FileAttributes.Hidden))
|
||||||
|
{
|
||||||
|
item.Items.Add(CreateItem(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
item.Items.Add(CreateErrorItem(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
item.EndInit();
|
||||||
|
FileSystem.Cursor = Cursors.Arrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeViewItem CreateErrorItem(Exception e)
|
||||||
|
{
|
||||||
|
var item = new TreeViewItem();
|
||||||
|
|
||||||
|
item.Foreground = Brushes.Red;
|
||||||
|
item.Header = $"{text.Get(TextKey.FileSystemDialog_LoadError)} {e.Message}";
|
||||||
|
item.ToolTip = e.GetType() + Environment.NewLine + e.StackTrace;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeViewItem CreateItem(DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
var header = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(2) };
|
||||||
|
var image = new Image
|
||||||
|
{
|
||||||
|
Height = 16,
|
||||||
|
Source = IconLoader.LoadIconFor(directory)
|
||||||
|
};
|
||||||
|
var item = new TreeViewItem();
|
||||||
|
var textBlock = new TextBlock { Margin = new Thickness(5, 0, 0, 0), Text = directory.Name, VerticalAlignment = VerticalAlignment.Center };
|
||||||
|
|
||||||
|
header.Children.Add(image);
|
||||||
|
header.Children.Add(textBlock);
|
||||||
|
|
||||||
|
item.Cursor = Cursors.Hand;
|
||||||
|
item.Header = header;
|
||||||
|
item.Tag = directory;
|
||||||
|
item.ToolTip = directory.FullName;
|
||||||
|
item.Items.Add(text.Get(TextKey.FileSystemDialog_Loading));
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeViewItem CreateItem(FileInfo file)
|
||||||
|
{
|
||||||
|
var header = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(2) };
|
||||||
|
var image = new Image
|
||||||
|
{
|
||||||
|
Height = 20,
|
||||||
|
Source = IconLoader.LoadIconFor(file)
|
||||||
|
};
|
||||||
|
var item = new TreeViewItem();
|
||||||
|
var textBlock = new TextBlock { Margin = new Thickness(5, 0, 0, 0), Text = file.Name, VerticalAlignment = VerticalAlignment.Center };
|
||||||
|
|
||||||
|
header.Children.Add(image);
|
||||||
|
header.Children.Add(textBlock);
|
||||||
|
|
||||||
|
item.Header = header;
|
||||||
|
item.Tag = file;
|
||||||
|
item.ToolTip = file.FullName;
|
||||||
|
|
||||||
|
if (element == FileSystemElement.File && operation == FileSystemOperation.Open)
|
||||||
|
{
|
||||||
|
item.Cursor = Cursors.Hand;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Cursor = Cursors.No;
|
||||||
|
item.Focusable = false;
|
||||||
|
textBlock.Foreground = Brushes.Gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeDialog()
|
||||||
|
{
|
||||||
|
CancelButton.Click += CancelButton_Click;
|
||||||
|
CancelButton.Content = text.Get(TextKey.FileSystemDialog_Cancel);
|
||||||
|
FileSystem.SelectedItemChanged += FileSystem_SelectedItemChanged;
|
||||||
|
NewElement.Visibility = operation == FileSystemOperation.Save ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
NewElementLabel.Text = text.Get(TextKey.FileSystemDialog_SaveAs);
|
||||||
|
NewElementName.KeyUp += NewElementName_KeyUp;
|
||||||
|
OperationIcon.Icon = operation == FileSystemOperation.Save ? FontAwesomeIcon.Download : FontAwesomeIcon.Search;
|
||||||
|
SelectButton.Click += SelectButton_Click;
|
||||||
|
SelectButton.Content = text.Get(TextKey.FileSystemDialog_Select);
|
||||||
|
|
||||||
|
InitializeText();
|
||||||
|
InitializeFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFileSystem()
|
||||||
|
{
|
||||||
|
foreach (var drive in DriveInfo.GetDrives())
|
||||||
|
{
|
||||||
|
FileSystem.Items.Add(CreateItem(drive.RootDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FileSystem.HasItems && FileSystem.Items[0] is TreeViewItem item)
|
||||||
|
{
|
||||||
|
item.IsSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(initialPath))
|
||||||
|
{
|
||||||
|
var pathRoot = Path.GetPathRoot(initialPath);
|
||||||
|
var directories = initialPath.Replace(pathRoot, "").Split(Path.DirectorySeparatorChar);
|
||||||
|
var segments = new List<string>();
|
||||||
|
|
||||||
|
segments.Add(pathRoot);
|
||||||
|
segments.AddRange(directories);
|
||||||
|
|
||||||
|
AutoSelect(FileSystem.Items, segments);
|
||||||
|
|
||||||
|
if (element == FileSystemElement.File && operation == FileSystemOperation.Save)
|
||||||
|
{
|
||||||
|
NewElementName.Text = Path.GetFileName(initialPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutoSelect(ItemCollection items, List<string> segments)
|
||||||
|
{
|
||||||
|
var segment = segments.FirstOrDefault();
|
||||||
|
|
||||||
|
if (segment != default(string))
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (item is TreeViewItem i && i.Tag is DirectoryInfo d && d.Name.Equals(segment))
|
||||||
|
{
|
||||||
|
i.IsExpanded = true;
|
||||||
|
i.IsSelected = true;
|
||||||
|
i.BringIntoView();
|
||||||
|
|
||||||
|
AutoSelect(i.Items, segments.Skip(1).ToList());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeText()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
if (element == FileSystemElement.File)
|
||||||
|
{
|
||||||
|
if (operation == FileSystemOperation.Open)
|
||||||
|
{
|
||||||
|
Message.Text = text.Get(TextKey.FileSystemDialog_OpenFileMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message.Text = text.Get(TextKey.FileSystemDialog_SaveFileMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (operation == FileSystemOperation.Open)
|
||||||
|
{
|
||||||
|
Message.Text = text.Get(TextKey.FileSystemDialog_OpenFolderMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message.Text = text.Get(TextKey.FileSystemDialog_SaveFolderMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message.Text = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(title))
|
||||||
|
{
|
||||||
|
Title = text.Get(TextKey.FileSystemDialog_Title);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -145,6 +145,9 @@
|
||||||
<Compile Include="Controls\TaskviewWindowControl.xaml.cs">
|
<Compile Include="Controls\TaskviewWindowControl.xaml.cs">
|
||||||
<DependentUpon>TaskviewWindowControl.xaml</DependentUpon>
|
<DependentUpon>TaskviewWindowControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="FileSystemDialog.xaml.cs">
|
||||||
|
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="FolderDialog.cs" />
|
<Compile Include="FolderDialog.cs" />
|
||||||
<Compile Include="LockScreen.xaml.cs">
|
<Compile Include="LockScreen.xaml.cs">
|
||||||
<DependentUpon>LockScreen.xaml</DependentUpon>
|
<DependentUpon>LockScreen.xaml</DependentUpon>
|
||||||
|
@ -322,6 +325,10 @@
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="FileSystemDialog.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="LockScreen.xaml">
|
<Page Include="LockScreen.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|
|
@ -23,6 +23,7 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -72,7 +73,12 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
|
||||||
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow)
|
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow)
|
||||||
{
|
{
|
||||||
return new BrowserWindow(control, settings, isMainWindow, text);
|
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
|
||||||
|
{
|
||||||
|
return Application.Current.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFolderDialog CreateFolderDialog(string message)
|
public IFolderDialog CreateFolderDialog(string message)
|
||||||
|
|
|
@ -23,6 +23,7 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -75,6 +76,11 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
return new BrowserWindow(control, settings, isMainWindow, text);
|
return new BrowserWindow(control, settings, isMainWindow, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public IFolderDialog CreateFolderDialog(string message)
|
public IFolderDialog CreateFolderDialog(string message)
|
||||||
{
|
{
|
||||||
return new FolderDialog(message);
|
return new FolderDialog(message);
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Utilities\IconLoader.cs" />
|
||||||
<Compile Include="Utilities\IconResourceLoader.cs" />
|
<Compile Include="Utilities\IconResourceLoader.cs" />
|
||||||
<Compile Include="Utilities\Thumbnail.cs" />
|
<Compile Include="Utilities\Thumbnail.cs" />
|
||||||
<Compile Include="Utilities\VisualExtensions.cs" />
|
<Compile Include="Utilities\VisualExtensions.cs" />
|
||||||
|
|
62
SafeExamBrowser.UserInterface.Shared/Utilities/IconLoader.cs
Normal file
62
SafeExamBrowser.UserInterface.Shared/Utilities/IconLoader.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Shared.Utilities
|
||||||
|
{
|
||||||
|
public static class IconLoader
|
||||||
|
{
|
||||||
|
private const uint SHGFI_ICON = 0x100;
|
||||||
|
private const uint SHGFI_LARGEICON = 0x0;
|
||||||
|
private const uint SHGFI_SMALLICON = 0x1;
|
||||||
|
|
||||||
|
public static ImageSource LoadIconFor(DirectoryInfo directory)
|
||||||
|
{
|
||||||
|
var fileInfo = new SHFILEINFO();
|
||||||
|
var result = SHGetFileInfo(directory.FullName, 0, ref fileInfo, (uint) Marshal.SizeOf(fileInfo), SHGFI_ICON | SHGFI_SMALLICON);
|
||||||
|
var imageSource = Imaging.CreateBitmapSourceFromHIcon(fileInfo.hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
||||||
|
|
||||||
|
DestroyIcon(fileInfo.hIcon);
|
||||||
|
|
||||||
|
return imageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImageSource LoadIconFor(FileInfo file)
|
||||||
|
{
|
||||||
|
using (var icon = System.Drawing.Icon.ExtractAssociatedIcon(file.FullName))
|
||||||
|
{
|
||||||
|
return Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int DestroyIcon(IntPtr hIcon);
|
||||||
|
|
||||||
|
[DllImport("shell32.dll")]
|
||||||
|
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct SHFILEINFO
|
||||||
|
{
|
||||||
|
public IntPtr hIcon;
|
||||||
|
public IntPtr iIcon;
|
||||||
|
public uint dwAttributes;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||||
|
public string szDisplayName;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
|
||||||
|
public string szTypeName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue