diff --git a/src/SIQuester/SIQuester.ViewModel/ContentItemsViewModel.cs b/src/SIQuester/SIQuester.ViewModel/ContentItemsViewModel.cs index 47c9050f..7e4322b3 100644 --- a/src/SIQuester/SIQuester.ViewModel/ContentItemsViewModel.cs +++ b/src/SIQuester/SIQuester.ViewModel/ContentItemsViewModel.cs @@ -1,5 +1,6 @@ using SIPackages; using SIPackages.Core; +using SIQuester.Model; using SIQuester.ViewModel.PlatformSpecific; using SIQuester.ViewModel.Properties; using System.Collections.Specialized; @@ -466,21 +467,33 @@ private bool AddAtomObject(string mediaType) index = -1; } - var atom = new ContentItemViewModel(new ContentItem + var contentItem = new ContentItemViewModel(new ContentItem { Type = mediaType, Value = "", Placement = mediaType == ContentTypes.Audio ? ContentPlacements.Background : ContentPlacements.Screen }); - Insert(index + 1, atom); + Insert(index + 1, contentItem); var last = collection.Files.LastOrDefault(); if (last != null) { - atom.Model.IsRef = true; - atom.Model.Value = last.Model.Name; + contentItem.Model.IsRef = true; + contentItem.Model.Value = last.Model.Name; + + if (AppSettings.Default.SetRightAnswerFromFileName) + { + var question = Owner; + + if (question.Right.Last().Length == 0) + { + question.Right.RemoveAt(question.Right.Count - 1); + } + + question.Right.Add(Path.GetFileNameWithoutExtension(last.Model.Name)); + } } document.ActiveItem = null; diff --git a/src/SIQuester/SIQuester.ViewModel/Model/AppSettings.cs b/src/SIQuester/SIQuester.ViewModel/Model/AppSettings.cs index 4f8b879d..b0436617 100644 --- a/src/SIQuester/SIQuester.ViewModel/Model/AppSettings.cs +++ b/src/SIQuester/SIQuester.ViewModel/Model/AppSettings.cs @@ -432,6 +432,25 @@ public bool CheckFileSize } } + private bool _setRightAnswerFromFileName = false; + + /// + /// Sets right answer when media file is added to question. + /// + [DefaultValue(false)] + public bool SetRightAnswerFromFileName + { + get => _setRightAnswerFromFileName; + set + { + if (_setRightAnswerFromFileName != value) + { + _setRightAnswerFromFileName = value; + OnPropertyChanged(); + } + } + } + private string? _language = null; /// @@ -559,5 +578,7 @@ internal void Reset() _flatScale = defaultSettings._flatScale; FlatLayoutMode = defaultSettings.FlatLayoutMode; SelectOptionCount = DefaultSelectOptionCount; + CheckFileSize = defaultSettings.CheckFileSize; + SetRightAnswerFromFileName = defaultSettings.SetRightAnswerFromFileName; } } diff --git a/src/SIQuester/SIQuester.ViewModel/PlatformSpecific/PlatformManager.cs b/src/SIQuester/SIQuester.ViewModel/PlatformSpecific/PlatformManager.cs index 6e181b32..e2bbc100 100644 --- a/src/SIQuester/SIQuester.ViewModel/PlatformSpecific/PlatformManager.cs +++ b/src/SIQuester/SIQuester.ViewModel/PlatformSpecific/PlatformManager.cs @@ -73,4 +73,6 @@ public abstract bool ShowSaveUI( public abstract bool ConfirmExclWithWindow(string message); public abstract void Exit(); + + public abstract string CompressImage(string imageUri); } diff --git a/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs b/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs index 9f39e7bc..97ae728c 100644 --- a/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs +++ b/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs @@ -1326,7 +1326,7 @@ internal QDocument( var msvmLogger = loggerFactory.CreateLogger(); - Images = new MediaStorageViewModel(this, Document.Images, Resources.Images, msvmLogger); + Images = new MediaStorageViewModel(this, Document.Images, Resources.Images, msvmLogger, true); Audio = new MediaStorageViewModel(this, Document.Audio, SIPackages.Properties.Resources.Audio, msvmLogger); Video = new MediaStorageViewModel(this, Document.Video, SIPackages.Properties.Resources.Video, msvmLogger); Html = new MediaStorageViewModel(this, Document.Html, SIPackages.Properties.Resources.Html, msvmLogger); diff --git a/src/SIQuester/SIQuester.ViewModel/Workspaces/Sidebar/MediaStorageViewModel.cs b/src/SIQuester/SIQuester.ViewModel/Workspaces/Sidebar/MediaStorageViewModel.cs index 41be6c0c..319eb255 100644 --- a/src/SIQuester/SIQuester.ViewModel/Workspaces/Sidebar/MediaStorageViewModel.cs +++ b/src/SIQuester/SIQuester.ViewModel/Workspaces/Sidebar/MediaStorageViewModel.cs @@ -2,6 +2,7 @@ using SIPackages; using SIPackages.Core; using SIQuester.ViewModel.Model; +using SIQuester.ViewModel.PlatformSpecific; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Input; @@ -90,6 +91,11 @@ public MediaItemViewModel? CurrentFile public ICommand DeleteItem { get; private set; } + /// + /// Compresses media item. + /// + public ICommand CompressItem { get; private set; } + private readonly string _header; private readonly string _name; @@ -120,7 +126,7 @@ public string Filter } } - public MediaStorageViewModel(QDocument document, DataCollection collection, string header, ILogger logger) + public MediaStorageViewModel(QDocument document, DataCollection collection, string header, ILogger logger, bool canCompress = false) { _document = document; _header = header; @@ -131,6 +137,7 @@ public MediaStorageViewModel(QDocument document, DataCollection collection, stri AddItem = new SimpleCommand(AddItem_Executed); DeleteItem = new SimpleCommand(Delete_Executed); + CompressItem = new SimpleCommand(CompressItem_Executed) { CanBeExecuted = canCompress }; } private void FillFiles(DataCollection collection) @@ -167,7 +174,7 @@ private void Named_PropertyChanged(object? sender, PropertyChangedEventArgs e) var newValue = item.Name; var renamedExisting = !_added.Any(mi => mi.Model == item); - Tuple tuple = null; + Tuple? tuple = null; if (renamedExisting) { @@ -286,25 +293,88 @@ private void Delete_Executed(object? arg) } } - private void PreviewRemove(MediaItemViewModel name) + private void CompressItem_Executed(object? arg) { - if (!Files.Contains(name)) + if (arg == null) { return; } - if (_added.Contains(name)) + var item = (MediaItemViewModel)arg; + + if (item.MediaSource == null) { - _added.Remove(name); - _removedStreams.Add(name, _streams[name]); - _streams.Remove(name); + return; + } + + var sourceUri = item.MediaSource.Uri; + var newUri = PlatformManager.Instance.CompressImage(sourceUri); + + if (newUri != sourceUri) + { + var newItem = CreateItem(item.Name); + var currentIndex = Files.IndexOf(item); + + var returnToCurrent = CurrentFile == item; + + PreviewRemove(item); + PreviewAdd(newItem, newUri, currentIndex); + HasPendingChanges = IsChanged(); + + if (returnToCurrent) + { + CurrentFile = newItem; + } + + OnChanged(new CustomChange( + () => + { + var returnToCurrent = CurrentFile == newItem; + + PreviewRemove(newItem); + PreviewAdd(item, sourceUri); + HasPendingChanges = IsChanged(); + + if (returnToCurrent) + { + CurrentFile = item; + } + }, + () => + { + var returnToCurrent = CurrentFile == item; + + PreviewRemove(item); + PreviewAdd(newItem, newUri); + HasPendingChanges = IsChanged(); + + if (returnToCurrent) + { + CurrentFile = newItem; + } + })); + } + } + + private void PreviewRemove(MediaItemViewModel item) + { + if (!Files.Contains(item)) + { + return; + } + + if (_added.Contains(item)) + { + _added.Remove(item); + _removedStreams.Add(item, _streams[item]); + _streams.Remove(item); } else { - _removed.Add(name); + _removed.Add(item); } - Files.Remove(name); + Files.Remove(item); OnPropertyChanged(nameof(Files)); } @@ -388,7 +458,7 @@ public async Task ApplyToAsync(DataCollection collection, bool final = false) private void AddItem_Executed(object? arg) { - var files = PlatformSpecific.PlatformManager.Instance.ShowMediaOpenUI(_name); + var files = PlatformManager.Instance.ShowMediaOpenUI(_name); if (files == null) { @@ -440,7 +510,7 @@ public MediaItemViewModel AddFile(string file, string? name = null) return item; } - private void PreviewAdd(MediaItemViewModel item, string path) + private void PreviewAdd(MediaItemViewModel item, string path, int index = -1) { if (_removed.Contains(item)) { @@ -463,9 +533,22 @@ private void PreviewAdd(MediaItemViewModel item, string path) _added.Add(item); _streams[item] = Tuple.Create(path, fileStream); + + if (_removedStreams.ContainsKey(item)) + { + _removedStreams.Remove(item); + } + } + + if (index == -1) + { + Files.Add(item); + } + else + { + Files.Insert(index, item); } - Files.Add(item); OnPropertyChanged(nameof(Files)); HasPendingChanges = IsChanged(); diff --git a/src/SIQuester/SIQuester/Implementation/DesktopManager.cs b/src/SIQuester/SIQuester/Implementation/DesktopManager.cs index 4c640505..90b125bb 100644 --- a/src/SIQuester/SIQuester/Implementation/DesktopManager.cs +++ b/src/SIQuester/SIQuester/Implementation/DesktopManager.cs @@ -14,6 +14,7 @@ using System.Windows; using System.Windows.Documents; using System.Windows.Media; +using System.Windows.Media.Imaging; using System.Windows.Shell; using System.Windows.Xps.Packaging; using System.Windows.Xps.Serialization; @@ -1024,6 +1025,55 @@ public override bool ConfirmExclWithWindow(string message) public override void Exit() => Application.Current.MainWindow?.Close(); + public override string CompressImage(string imageUri) + { + var extension = Path.GetExtension(imageUri); + + if (extension != ".jpg" && extension != ".png") + { + return imageUri; + } + + var bitmapImage = new BitmapImage(new Uri(imageUri)); + + var width = bitmapImage.PixelWidth; + var height = bitmapImage.PixelHeight; + + const double TargetPixelSize = 800.0; + const int TargetQualityLevel = 90; + + if (width <= TargetPixelSize && height <= TargetPixelSize) + { + return imageUri; + } + + var widthScale = TargetPixelSize / width; + var heightScale = TargetPixelSize / height; + + var scale = Math.Min(widthScale, heightScale); + + var resizedImage = new TransformedBitmap(bitmapImage, new ScaleTransform(scale, scale)); + + BitmapEncoder encoder = extension == ".jpg" ? new JpegBitmapEncoder + { + QualityLevel = TargetQualityLevel + } : new PngBitmapEncoder(); + + encoder.Frames.Add(BitmapFrame.Create(resizedImage)); + + var fileName = Path.GetFileName(imageUri); + var outputDir = Path.Combine(Path.GetTempPath(), AppSettings.ProductName, AppSettings.MediaFolderName, Guid.NewGuid().ToString()); + Directory.CreateDirectory(outputDir); + var outputPath = Path.Combine(outputDir, fileName); + + using (var fileStream = new FileStream(outputPath, FileMode.Create)) + { + encoder.Save(fileStream); + } + + return outputPath; + } + public void Dispose() { diff --git a/src/SIQuester/SIQuester/Properties/Resources.Designer.cs b/src/SIQuester/SIQuester/Properties/Resources.Designer.cs index 4d54ab7b..98f1b139 100644 --- a/src/SIQuester/SIQuester/Properties/Resources.Designer.cs +++ b/src/SIQuester/SIQuester/Properties/Resources.Designer.cs @@ -2536,6 +2536,15 @@ public static string SetLogo { } } + /// + /// Ищет локализованную строку, похожую на Задавать правильный ответ по имени файла, добавляемого в вопрос. + /// + public static string SetRightAnswerFromFileName { + get { + return ResourceManager.GetString("SetRightAnswerFromFileName", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Задать теги. /// diff --git a/src/SIQuester/SIQuester/Properties/Resources.en-US.resx b/src/SIQuester/SIQuester/Properties/Resources.en-US.resx index c6d8eaa2..a59cc18f 100644 --- a/src/SIQuester/SIQuester/Properties/Resources.en-US.resx +++ b/src/SIQuester/SIQuester/Properties/Resources.en-US.resx @@ -939,6 +939,9 @@ Set logo + + Set right answer by name of file which is added to question + Set tags diff --git a/src/SIQuester/SIQuester/Properties/Resources.resx b/src/SIQuester/SIQuester/Properties/Resources.resx index 44264931..353a3e68 100644 --- a/src/SIQuester/SIQuester/Properties/Resources.resx +++ b/src/SIQuester/SIQuester/Properties/Resources.resx @@ -943,6 +943,9 @@ Задать логотип + + Задавать правильный ответ по имени файла, добавляемого в вопрос + Задать теги diff --git a/src/SIQuester/SIQuester/View/FlatDocView.xaml.cs b/src/SIQuester/SIQuester/View/FlatDocView.xaml.cs index d39d34d5..a0667d23 100644 --- a/src/SIQuester/SIQuester/View/FlatDocView.xaml.cs +++ b/src/SIQuester/SIQuester/View/FlatDocView.xaml.cs @@ -555,6 +555,18 @@ private void TryImportMedia(DragEventArgs e, string filePath, string mediaType) Value = item.Model.Name, Placement = contentType == ContentTypes.Audio ? ContentPlacements.Background : ContentPlacements.Screen })); + + if (AppSettings.Default.SetRightAnswerFromFileName) + { + var question = contentItemsViewModel.Owner; + + if (question.Right.Last().Length == 0) + { + question.Right.RemoveAt(question.Right.Count - 1); + } + + question.Right.Add(Path.GetFileNameWithoutExtension(item.Model.Name)); + } } internal static void RecountPrices(Theme theme, int pos, bool down) diff --git a/src/SIQuester/SIQuester/View/MediaStorageView.xaml b/src/SIQuester/SIQuester/View/MediaStorageView.xaml index 4a0ba3d7..e48ebdf4 100644 --- a/src/SIQuester/SIQuester/View/MediaStorageView.xaml +++ b/src/SIQuester/SIQuester/View/MediaStorageView.xaml @@ -214,10 +214,9 @@ Data="{Binding Source={StaticResource app_delete},Path=Data}" /> - + + + + + + diff --git a/src/SIQuester/SIQuester/View/TreeDocView.xaml.cs b/src/SIQuester/SIQuester/View/TreeDocView.xaml.cs index fc9a4b63..bd6387b2 100644 --- a/src/SIQuester/SIQuester/View/TreeDocView.xaml.cs +++ b/src/SIQuester/SIQuester/View/TreeDocView.xaml.cs @@ -471,6 +471,18 @@ private void TryImportMedia(DragEventArgs e, string filePath, string mediaType) Value = item.Model.Name, Placement = contentType == ContentTypes.Audio ? ContentPlacements.Background : ContentPlacements.Screen })); + + if (AppSettings.Default.SetRightAnswerFromFileName) + { + var question = contentItemsViewModel.Owner; + + if (question.Right.Last().Length == 0) + { + question.Right.RemoveAt(question.Right.Count - 1); + } + + question.Right.Add(Path.GetFileNameWithoutExtension(item.Model.Name)); + } } private static bool AreEqual(Question question1, Question question2)