using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Drawing; using System.Linq; using System.Reflection; using System.Windows.Forms; using Microsoft.Office.Interop.PowerPoint; using PowerPointLabs.ActionFramework.Common.Log; using PowerPointLabs.AutoUpdate.Interface; using PowerPointLabs.Models; using PowerPointLabs.PictureSlidesLab.Model; using PowerPointLabs.PictureSlidesLab.ModelFactory; using PowerPointLabs.PictureSlidesLab.Service; using PowerPointLabs.PictureSlidesLab.Service.Effect; using PowerPointLabs.PictureSlidesLab.Service.Interface; using PowerPointLabs.PictureSlidesLab.Service.Preview; using PowerPointLabs.PictureSlidesLab.Thread; using PowerPointLabs.PictureSlidesLab.Util; using PowerPointLabs.PictureSlidesLab.ViewModel.SliderPropHandler.Factory; using PowerPointLabs.PictureSlidesLab.Views.Interface; using PowerPointLabs.TextCollection; using PowerPointLabs.Utils; using PowerPointLabs.WPF.Observable; using Fonts = System.Windows.Media.Fonts; namespace PowerPointLabs.PictureSlidesLab.ViewModel { partial class PictureSlidesLabWindowViewModel { #region UI Models // UI model - for image selection stage public ObservableCollection<ImageItem> ImageSelectionList { get; set; } public ObservableInt ImageSelectionListSelectedId { get; set; } public ObservableImageItem ImageSelectionListSelectedItem { get; set; } public ObservableBoolean IsActiveDownloadProgressRing { get; set; } // UI model - for preview stage public ObservableCollection<ImageItem> StylesPreviewList { get; set; } public ObservableInt StylesPreviewListSelectedId { get; set; } public ObservableImageItem StylesPreviewListSelectedItem { get; set; } // UI model - for variation stage public ObservableCollection<ImageItem> StylesVariationList { get; set; } public ObservableInt StylesVariationListSelectedId { get; set; } public ObservableImageItem StylesVariationListSelectedItem { get; set; } public ObservableString CurrentVariantCategory { get; set; } public ObservableInt CurrentVariantCategoryId { get; set; } public ObservableCollection<string> VariantsCategory { get; set; } public ObservableCollection<string> FontFamilies { get; set; } public ObservableInt SelectedFontId { get; set; } public ObservableFont SelectedFontFamily { get; set; } public ObservableInt SelectedSliderValue { get; set; } public ObservableBoolean IsSliderValueChanged { get; set; } public ObservableInt SelectedSliderMaximum { get; set; } public ObservableInt SelectedSliderTickFrequency { get; set; } public Settings Settings { get; set; } #endregion #region Dependency // UI controller public IPictureSlidesLabWindowView View { private get; set; } // Downloader public IDownloader ImageDownloader { private get; set; } // Background presentation that will do the styles processing public IStylesDesigner Designer { private get; set; } // Style Options Factory public StyleOptionsFactory OptionsFactory { get; set; } // Style Variants Factory public StyleVariantsFactory VariantsFactory { get; set; } [Import(typeof(SliderPropHandlerFactory))] private SliderPropHandlerFactory PropHandlerFactory { get; set; } #endregion #region States for variation stage private string _previousVariantsCategory; private List<StyleOption> _styleOptions; // key - variant category, value - variants private Dictionary<string, List<StyleVariant>> _styleVariants; // for picture variation private List<ImageItem> _8PicturesInPictureVariation; private bool _isPictureVariationInit; #endregion #region Lifecycle public PictureSlidesLabWindowViewModel(IPictureSlidesLabWindowView view, IStylesDesigner stylesDesigner = null) { Logger.Log("Init PSL View Model begins"); View = view; ImageDownloader = new ContextDownloader(View.GetThreadContext()); InitStorage(); InitUiModels(); InitFontFamilies(); CleanUnusedPersistentData(); Designer = stylesDesigner ?? new StylesDesigner(); Designer.SetSettings(Settings); OptionsFactory = new StyleOptionsFactory(); VariantsFactory = new StyleVariantsFactory(); var catalog = new AggregateCatalog( new AssemblyCatalog(Assembly.GetExecutingAssembly())); var container = new CompositionContainer(catalog); container.ComposeParts(this); Logger.Log("Init PSL View Model done"); } public void CleanUp() { if (Designer != null) { Designer.CleanUp(); } ImageSelectionList.RemoveAt(0); StoragePath.Save(ImageSelectionList); StoragePath.Save(Settings); Logger.Log("ViewModel clean up done"); } #endregion #region Stage - Image Selection (Add Image) public void RemoveAllImageSelectionListItems() { ImageSelectionList.Clear(); ImageSelectionList.Add(CreateChoosePicturesItem()); Logger.Log("Clear all images done"); } /// <summary> /// Add image from local files /// </summary> /// <param name="filenames"></param> /// <param name="contentSlide"></param> /// <param name="slideWidth"></param> /// <param name="slideHeight"></param> public void AddImageSelectionListItem(string[] filenames, Slide contentSlide, float slideWidth, float slideHeight) { try { Logger.Log("Add local picture begins"); var isToSelectPicture = ImageSelectionList.Count == 1; foreach (var filename in filenames) { VerifyIsProperImage(filename); var fromFileItem = new ImageItem { ImageFile = ImageUtil.GetThumbnailFromFullSizeImg(filename), FullSizeImageFile = filename, ContextLink = filename, Source = "local drive", Tooltip = ImageUtil.GetWidthAndHeight(filename) }; //add it ImageSelectionList.Add(fromFileItem); UpdatePictureInPictureVariationWhenAddedNewOne(fromFileItem); } if (IsInPictureVariation() && filenames.Length > 0) { UpdatePreviewImages( ImageSelectionListSelectedItem.ImageItem ?? View.CreateDefaultPictureItem(), contentSlide, slideWidth, slideHeight); } if (isToSelectPicture) { ImageSelectionListSelectedId.Number = 1; } Logger.Log("Add local picture done"); } catch (Exception e) { // not an image or image is corrupted View.ShowErrorMessageBox(PictureSlidesLabText.ErrorImageCorrupted); Logger.LogException(e, "AddImageSelectionListItem"); } } /// <summary> /// Add image by downloading /// </summary> /// <param name="downloadLink"></param> /// <param name="contentSlide"></param> /// <param name="slideWidth"></param> /// <param name="slideHeight"></param> public void AddImageSelectionListItem(string downloadLink, Slide contentSlide, float slideWidth, float slideHeight) { if (StringUtil.IsEmpty(downloadLink) || !UrlUtil.IsUrlValid(downloadLink)) // Case 1: If url not valid { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorUrlLinkIncorrect); Logger.Log("Url link error when add internet image"); return; } var item = new ImageItem { ImageFile = StoragePath.LoadingImgPath, ContextLink = downloadLink, Source = downloadLink }; UrlUtil.GetMetaInfo(ref downloadLink, item); ImageSelectionList.Add(item); IsActiveDownloadProgressRing.Flag = true; var imagePath = StoragePath.GetPath("img-" + DateTime.Now.GetHashCode() + "-" + Guid.NewGuid().ToString().Substring(0, 7)); ImageDownloader .Get(downloadLink, imagePath) .After((AutoUpdate.Downloader.AfterDownloadEventDelegate)(() => { try { Logger.Log("Add internet picture begins"); VerifyIsProperImage(imagePath); // Case 2: not a proper image item.UpdateDownloadedImage(imagePath); UpdatePictureInPictureVariationWhenAddedNewOne(item); if (ImageSelectionListSelectedItem.ImageItem != null && imagePath == ImageSelectionListSelectedItem.ImageItem.FullSizeImageFile) { UpdatePreviewImages(ImageSelectionListSelectedItem.ImageItem, contentSlide, slideWidth, slideHeight); } else if (IsInPictureVariation()) { UpdatePreviewImages( ImageSelectionListSelectedItem.ImageItem ?? View.CreateDefaultPictureItem(), contentSlide, slideWidth, slideHeight); } Logger.Log("Add internet picture ends"); } catch (Exception e) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorImageDownloadCorrupted); ImageSelectionList.Remove(item); Logger.LogException(e, "AddImageSelectionListItem (download)"); } finally { IsActiveDownloadProgressRing.Flag = false; } })) // Case 3: Possibly network timeout .OnError((AutoUpdate.Downloader.ErrorEventDelegate)(e => { IsActiveDownloadProgressRing.Flag = false; ImageSelectionList.Remove(item); View.ShowErrorMessageBox(PictureSlidesLabText.ErrorFailedToLoad + e.Message); })) .Start(); } #endregion #region Stage - Styles Previewing /// <summary> /// General update preview images, /// can be used in most use cases, such as reload preview images /// after re-activate PSL main window. /// </summary> /// <param name="source"></param> /// <param name="contentSlide"></param> /// <param name="slideWidth"></param> /// <param name="slideHeight"></param> public void UpdatePreviewImages(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight, bool isUpdateSelectedPreviewOnly = false) { if (View.IsVariationsFlyoutOpen) { Logger.Log("Generate preview images for variation stage"); UpdateStylesVariationImagesAfterOpenFlyout(source, contentSlide, slideWidth, slideHeight, isUpdateSelectedPreviewOnly); Logger.Log("Generate preview images for variation stage done"); } else { Logger.Log("Generate preview images for preview style stage"); UpdateStylesPreviewImages(source, contentSlide, slideWidth, slideHeight); Logger.Log("Generate preview images for preview style stage done"); } } public void ApplyStyleInPreviewStage(Slide contentSlide, float slideWidth, float slideHeight) { Logger.Log("Apply style in preview stage begins"); var copiedPicture = LoadClipboardPicture(); try { var targetDefaultOptions = OptionsFactory .GetStylesPreviewOption(StylesPreviewListSelectedItem.ImageItem.Tooltip); Designer.ApplyStyle(ImageSelectionListSelectedItem.ImageItem, contentSlide, slideWidth, slideHeight, targetDefaultOptions); View.ShowSuccessfullyAppliedDialog(); } catch (Exception e) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorNoSelectedSlide); Logger.LogException(e, "ApplyStyleInPreviewStage"); } SaveClipboardPicture(copiedPicture); Logger.Log("Apply style in preview stage done"); } #endregion #region Stage - Styles Variation /// <summary> /// When stylesVariationListSelectedItem is changed, /// this method will be called to update the corresponding style options of designer /// </summary> public void UpdateStyleVariationStyleOptionsWhenSelectedItemChange() { try { Designer.SetStyleOptions(_styleOptions[StylesVariationListSelectedId.Number]); } catch (Exception e) { View.ShowErrorMessageBox("Failed when retrieving information from the selected preview image.", e); Logger.LogException(e, "UpdateStyleVariationStyleOptionsWhenSelectedItemChange"); } } /// <summary> /// Update styles variation iamges before its flyout is open /// </summary> /// <param name="source"></param> /// <param name="contentSlide"></param> /// <param name="slideWidth"></param> /// <param name="slideHeight"></param> /// <param name="givenOptions"></param> /// <param name="givenVariants"></param> public void UpdateStyleVariationImagesWhenOpenFlyout(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight, List<StyleOption> givenOptions = null, Dictionary<string, ListgivenVariants = null) { Logger.Log("Variation stage is open"); var targetStyleItem = StylesPreviewListSelectedItem.ImageItem; StylesVariationList.Clear(); if (!IsAbleToUpdateStylesVariationImages(source, targetStyleItem, contentSlide)) { return; } InitStylesVariationCategories(givenOptions, givenVariants, targetStyleItem.Tooltip); if (Settings.GetDefaultAspectWhenCustomize() == Aspect.PictureAspect) { UpdateStylesVariationImages(source, contentSlide, slideWidth, slideHeight, isMockPreviewImages: true); } else { UpdateStylesVariationImages(source, contentSlide, slideWidth, slideHeight); } StylesVariationListSelectedId.Number = 0; View.SetVariationListBoxScrollOffset(0d); _isPictureVariationInit = false; if (Settings.GetDefaultAspectWhenCustomize() == Aspect.PictureAspect) { CurrentVariantCategoryId.Number = VariantsCategory.IndexOf(PictureSlidesLabText.VariantCategoryPicture); } Logger.Log("Variation open completed"); } /// <summary> /// Update styles variation images after its flyout been open /// </summary> public void UpdateStylesVariationImagesAfterOpenFlyout(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight, bool isUpdateSelectedPreviewOnly = false) { Logger.Log("Variation is already open, update preview images"); var selectedId = StylesVariationListSelectedId.Number; var scrollOffset = View.GetVariationListBoxScrollOffset(); var targetStyleItem = StylesPreviewListSelectedItem.ImageItem; if (!isUpdateSelectedPreviewOnly) { StylesVariationList.Clear(); } if (!IsAbleToUpdateStylesVariationImages(source, targetStyleItem, contentSlide)) { return; } StylesVariationListSelectedId.Number = selectedId < 0 ? 0 : selectedId; if (isUpdateSelectedPreviewOnly) { UpdateStylesVariationImages(source, contentSlide, slideWidth, slideHeight, selectedId: StylesVariationListSelectedId.Number); } else { UpdateStylesVariationImages(source, contentSlide, slideWidth, slideHeight); } View.SetVariationListBoxScrollOffset(scrollOffset); } /// <summary> /// This method implements the way to guide the user step by step to customize /// style /// </summary> public void UpdateStepByStepStylesVariationImages(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight) { Logger.Log("Check for step by step preview"); Logger.Log("current variation list selected id is " + StylesVariationListSelectedId.Number); Logger.Log("variants category count is " + VariantsCategory.Count); if (StylesVariationListSelectedId.Number < 0 || VariantsCategory.Count == 0) { return; } Logger.Log("Step by step preview begins"); var targetVariationSelectedIndex = StylesVariationListSelectedId.Number; var targetVariant = _styleVariants[_previousVariantsCategory][targetVariationSelectedIndex]; foreach (var option in _styleOptions) { targetVariant.Apply(option); } var currentVariantsCategory = CurrentVariantCategory.Text; if (currentVariantsCategory != PictureSlidesLabText.VariantCategoryFontColor && _previousVariantsCategory != PictureSlidesLabText.VariantCategoryFontColor) { // apply font color variant, // because default styles may contain special font color settings, but not in variants var fontColorVariant = new StyleVariant(new Dictionary<string, object> { {"FontColor", _styleOptions[targetVariationSelectedIndex].FontColor} }); foreach (var option in _styleOptions) { fontColorVariant.Apply(option); } } var nextCategoryVariants = _styleVariants[currentVariantsCategory]; if (currentVariantsCategory == PictureSlidesLabText.VariantCategoryFontFamily) { var isFontInVariation = false; var currentFontFamily = _styleOptions[targetVariationSelectedIndex].FontFamily; foreach (var variant in nextCategoryVariants) { if (currentFontFamily == (string) variant.Get("FontFamily")) { isFontInVariation = true; } } if (!isFontInVariation && targetVariationSelectedIndex >= 0 && targetVariationSelectedIndex < nextCategoryVariants.Count) { nextCategoryVariants[targetVariationSelectedIndex] .Set("FontFamily", currentFontFamily); nextCategoryVariants[targetVariationSelectedIndex] .Set("OptionName", currentFontFamily); } } int pictureIndexToSelect = -1; // Enter picture variation for the first time if (CurrentVariantCategory.Text == PictureSlidesLabText.VariantCategoryPicture && !_isPictureVariationInit) { _8PicturesInPictureVariation = GetLast8Pictures(targetVariationSelectedIndex); _isPictureVariationInit = true; } // Enter picture variation again else if (CurrentVariantCategory.Text == PictureSlidesLabText.VariantCategoryPicture && _isPictureVariationInit) { var isPictureSwapped = false; for (var i = 0; i < _8PicturesInPictureVariation.Count; i++) { // swap the picture to the current selected id in // variation list var picture = _8PicturesInPictureVariation[i]; if ((ImageSelectionListSelectedItem.ImageItem == null && picture.ImageFile == StoragePath.NoPicturePlaceholderImgPath) || (ImageSelectionListSelectedItem.ImageItem != null && picture.ImageFile == ImageSelectionListSelectedItem.ImageItem.ImageFile)) { var tempPic = _8PicturesInPictureVariation[targetVariationSelectedIndex]; _8PicturesInPictureVariation[targetVariationSelectedIndex] = picture; _8PicturesInPictureVariation[i] = tempPic; isPictureSwapped = true; break; } } if (!isPictureSwapped) { // if the current picture doesn't exist in the _8PicturesInPictureVariation // directly overwrite the existing one at the selected id UpdateSelectedPictureInPictureVariation(); } } // Exit picture variation else if (_previousVariantsCategory == PictureSlidesLabText.VariantCategoryPicture) { // use the selected picture in the picture variation to preview var targetPicture = _8PicturesInPictureVariation[targetVariationSelectedIndex]; if (targetPicture.ImageFile != StoragePath.NoPicturePlaceholderImgPath) { var indexForTargetPicture = ImageSelectionList.IndexOf(targetPicture); if (indexForTargetPicture == -1) { ImageSelectionList.Add(targetPicture); pictureIndexToSelect = ImageSelectionList.Count - 1; } else { pictureIndexToSelect = indexForTargetPicture; } } else // target picture is the default picture { // enter default picture mode View.EnterDefaultPictureMode(); source = View.CreateDefaultPictureItem(); } } var variantIndexWithoutEffect = -1; for (var i = 0; i < nextCategoryVariants.Count; i++) { if (nextCategoryVariants[i].IsNoEffect(_styleOptions[targetVariationSelectedIndex])) { variantIndexWithoutEffect = i; break; } } // swap the no-effect variant with the current selected style's corresponding variant // so that to achieve an effect: jumpt between different category wont change the // selected style if (variantIndexWithoutEffect != -1) { var temp = nextCategoryVariants[variantIndexWithoutEffect]; nextCategoryVariants[variantIndexWithoutEffect] = nextCategoryVariants[targetVariationSelectedIndex]; nextCategoryVariants[targetVariationSelectedIndex] = temp; } for (var i = 0; i < nextCategoryVariants.Count && i < _styleOptions.Count; i++) { nextCategoryVariants[i].Apply(_styleOptions[i]); } _previousVariantsCategory = currentVariantsCategory; Logger.Log("picture index to select is " + pictureIndexToSelect); if (pictureIndexToSelect == -1 || pictureIndexToSelect == ImageSelectionListSelectedId.Number) { UpdateStylesVariationImagesAfterOpenFlyout(source, contentSlide, slideWidth, slideHeight); } else { ImageSelectionListSelectedId.Number = pictureIndexToSelect; } Logger.Log("Step by step preview done"); } public void ApplyStyleInVariationStage(Slide contentSlide, float slideWidth, float slideHeight) { Logger.Log("Apply style in variation stage begins"); var copiedPicture = LoadClipboardPicture(); try { Designer.ApplyStyle( IsInPictureVariation() ? GetSelectedPictureInPictureVariation( StylesVariationListSelectedId.Number) : ImageSelectionListSelectedItem.ImageItem, contentSlide, slideWidth, slideHeight); if (IsInPictureVariation()) { // select the picture if possible var targetPicture = GetSelectedPictureInPictureVariation( StylesVariationListSelectedId.Number); if (targetPicture.ImageFile != StoragePath.NoPicturePlaceholderImgPath) { var indexForTargetPicture = ImageSelectionList.IndexOf(targetPicture); if (indexForTargetPicture == -1) { ImageSelectionList.Add(targetPicture); ImageSelectionListSelectedId.Number = ImageSelectionList.Count - 1; } else { ImageSelectionListSelectedId.Number = indexForTargetPicture; } } } View.ShowSuccessfullyAppliedDialog(); } catch (Exception e) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorNoSelectedSlide); Logger.LogException(e, "ApplyStyleInVariationStage"); } SaveClipboardPicture(copiedPicture); Logger.Log("Apply style in variation stage done"); } #region Picture Variation public bool IsInPictureVariation() { return CurrentVariantCategory != null && CurrentVariantCategory.Text != null && CurrentVariantCategory.Text == PictureSlidesLabText.VariantCategoryPicture; } public ImageItem GetSelectedPictureInPictureVariation(int pictureIndex) { try { return _8PicturesInPictureVariation[pictureIndex]; } catch (Exception e) { View.ShowErrorMessageBox("Failed when fetching picture aspect.", e); Logger.LogException(e, "GetSelectedPictureInPictureVariation"); return View.CreateDefaultPictureItem(); } } public void UpdateSelectedPictureInPictureVariation() { Logger.Log("Check for update selected picture in picture aspect"); Logger.Log("is in picture aspect: " + IsInPictureVariation()); Logger.Log("variation list selectedId is: " + StylesVariationListSelectedId.Number); if (!IsInPictureVariation() || StylesVariationListSelectedId.Number == -1) { return; } try { _8PicturesInPictureVariation[StylesVariationListSelectedId.Number] = ImageSelectionListSelectedItem.ImageItem ?? View.CreateDefaultPictureItem(); } catch (Exception e) { View.ShowErrorMessageBox("Failed when processing picture aspect.", e); Logger.LogException(e, "UpdateSelectedPictureInPictureVariation"); } } public void UpdatePictureInPictureVariationWhenAddedNewOne(ImageItem newPicture) { Logger.Log("Check for update picture in picture aspect when added new one"); Logger.Log("is in picture aspect: " + IsInPictureVariation()); Logger.Log("new pic is null: " + (newPicture == null)); if (!IsInPictureVariation() || newPicture == null) { return; } for (var i = 0; i < _8PicturesInPictureVariation.Count; i++) { var imageItem = _8PicturesInPictureVariation[i]; if (imageItem.ImageFile == StoragePath.NoPicturePlaceholderImgPath) { _8PicturesInPictureVariation[i] = newPicture; break; } } } public void UpdatePictureInPictureVariationWhenDeleteSome() { Logger.Log("Check for update picture in picture aspect when deleted some"); Logger.Log("is in picture aspect: " + IsInPictureVariation()); if (!IsInPictureVariation()) { return; } for (var i = 0; i < _8PicturesInPictureVariation.Count; i++) { var imageItem = _8PicturesInPictureVariation[i]; if (ImageSelectionList.IndexOf(imageItem) == -1) { _8PicturesInPictureVariation[i] = View.CreateDefaultPictureItem(); } } } public void RefreshLast8Pictures() { var selectedIdOfVariationList = Math.Max(StylesVariationListSelectedId.Number, 0); _8PicturesInPictureVariation = GetLast8Pictures(selectedIdOfVariationList); } #endregion #endregion #region Add Picture Citation Slide public void AddPictureCitationSlide(Slide slide, List<PowerPointSlide> allSlides) { new PictureCitationSlide(slide, allSlides).CreatePictureCitations(); } #endregion #region Helper funcs private List<ImageItem> GetLast8Pictures(int selectedIdOfVariationList) { if (!IsInPictureVariation()) { return new List<ImageItem>(); } try { var subPictureList = ImageSelectionList.Skip(Math.Max(1, ImageSelectionList.Count - 8)); var result = new List<ImageItem>(subPictureList); while (result.Count < 8) { result.Add(View.CreateDefaultPictureItem()); } if (ImageSelectionListSelectedItem.ImageItem != null && !result.Contains(ImageSelectionListSelectedItem.ImageItem)) { result[selectedIdOfVariationList] = ImageSelectionListSelectedItem.ImageItem; } else if (ImageSelectionListSelectedItem.ImageItem == null) { for (var i = 0; i < result.Count; i++) { if (result[i].ImageFile == StoragePath.NoPicturePlaceholderImgPath) { result[i] = result[selectedIdOfVariationList]; break; } } result[selectedIdOfVariationList] = View.CreateDefaultPictureItem(); } else if (selectedIdOfVariationList >= 0) // contains selected item, need swap to selected index { var indexToSwap = result.IndexOf(ImageSelectionListSelectedItem.ImageItem); var tempItem = result[selectedIdOfVariationList]; result[selectedIdOfVariationList] = ImageSelectionListSelectedItem.ImageItem; result[indexToSwap] = tempItem; } return result; } catch (Exception e) { View.ShowErrorMessageBox("Failed when generating picture aspect.", e); Logger.LogException(e, "GetLast8Pictures"); return new List<ImageItem>(); } } private static IList<object> LoadClipboardPicture() { try { Logger.Log("Load clipboard begins."); var result = new List<object>(); if (Clipboard.ContainsImage()) { result.Add(Clipboard.GetImage()); } if (Clipboard.ContainsFileDropList()) { result.Add(Clipboard.GetFileDropList()); } if (Clipboard.ContainsText()) { result.Add(Clipboard.GetText()); } Logger.Log("Load clipboard done."); return result; } catch (Exception e) { // sometimes Clipboard may fail Logger.LogException(e, "LoadClipboardPicture"); return new List<object>(); } } private static void SaveClipboardPicture(IList<object> copiedObjs) { try { Logger.Log("Save clipboard begins."); foreach (var copiedObj in copiedObjs) { if (copiedObj == null) { continue; } if (copiedObj is Image) { Clipboard.SetImage((Image)copiedObj); } else if (copiedObj is StringCollection) { Clipboard.SetFileDropList((StringCollection)copiedObj); } else if (!string.IsNullOrEmpty(copiedObj as string)) { Clipboard.SetText((string)copiedObj); } } Logger.Log("Save clipboard done."); } catch (Exception e) { // sometimes Clipboard may fail Logger.LogException(e, "SaveClipboardPicture"); } } private static void VerifyIsProperImage(string filename) { using (Image.FromFile(filename)) { // so this is a proper image } } private void UpdateStylesPreviewImages(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight) { Logger.Log("UpdateStylesPreviewImages begins"); var selectedId = StylesPreviewListSelectedId.Number; StylesPreviewList.Clear(); if (!IsAbleToUpdateStylesPreviewImages(source, contentSlide)) { return; } var copiedPicture = LoadClipboardPicture(); try { var allStyleOptions = OptionsFactory.GetAllStylesPreviewOptions(); Logger.Log("Number of styles: " + allStyleOptions.Count); foreach (var stylesPreviewOption in allStyleOptions) { var previewInfo = Designer.PreviewApplyStyle(source, contentSlide, slideWidth, slideHeight, stylesPreviewOption); StylesPreviewList.Add(new ImageItem { ImageFile = previewInfo.PreviewApplyStyleImagePath, Tooltip = stylesPreviewOption.StyleName }); } GC.Collect(); } catch (Exception e) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorImageCorrupted, e); Logger.LogException(e, "UpdateStylesPreviewImages"); } SaveClipboardPicture(copiedPicture); StylesPreviewListSelectedId.Number = selectedId < 0 ? 0 : selectedId; Logger.Log("UpdateStylesPreviewImages done"); } private static bool IsAbleToUpdateStylesPreviewImages(ImageItem source, Slide contentSlide) { Logger.Log("Check for update styles in preview styles stage"); Logger.Log("source is null: " + (source == null)); Logger.Log("source is loading img: " + (source != null && source.ImageFile == StoragePath.LoadingImgPath)); Logger.Log("content slide is null: " + (contentSlide == null)); return !(source == null || source.ImageFile == StoragePath.LoadingImgPath || contentSlide == null); } private void InitStylesVariationCategories(List<StyleOption> givenOptions, Dictionary<string, List<StyleVariant>> givenVariants, string targetStyle) { Logger.Log("Init variation stage begins"); _styleOptions = givenOptions ?? OptionsFactory.GetStylesVariationOptions(targetStyle); _styleVariants = givenVariants ?? VariantsFactory.GetVariants(targetStyle); VariantsCategory.Clear(); foreach (var styleVariant in _styleVariants.Keys) { VariantsCategory.Add(styleVariant); } CurrentVariantCategoryId.Number = 0; _previousVariantsCategory = VariantsCategory[0]; // default style options (in preview stage) var defaultStyleOptions = OptionsFactory.GetStylesPreviewOption(targetStyle); var currentVariants = _styleVariants.Values.First(); var variantIndexWithoutEffect = -1; for (var i = 0; i < currentVariants.Count; i++) { if (currentVariants[i].IsNoEffect(defaultStyleOptions)) { variantIndexWithoutEffect = i; break; } } // swap the no-effect variant with the current selected style's corresponding variant // so that to achieve continuity. // in order to swap, style option provided from StyleOptionsFactory should have // corresponding values specified in StyleVariantsFactory. e.g., an option generated // from factory has overlay transparency of 35, then in order to swap, it should have // a variant of overlay transparency of 35. Otherwise it cannot swap, because variants // don't match any values in the style options. if (variantIndexWithoutEffect != -1 && givenOptions == null) { // swap style variant var tempVariant = currentVariants[variantIndexWithoutEffect]; currentVariants[variantIndexWithoutEffect] = currentVariants[0]; currentVariants[0] = tempVariant; // swap default style options (in variation stage) var tempStyleOpt = _styleOptions[variantIndexWithoutEffect]; _styleOptions[variantIndexWithoutEffect] = _styleOptions[0]; _styleOptions[0] = tempStyleOpt; } for (var i = 0; i < currentVariants.Count && i < _styleOptions.Count; i++) { currentVariants[i].Apply(_styleOptions[i]); } Logger.Log("Init variation stage done"); } private static bool IsAbleToUpdateStylesVariationImages(ImageItem source, ImageItem targetStyleItem, Slide contentSlide) { Logger.Log("Check for update styles in variation stage"); Logger.Log("source is null: " + (source == null)); Logger.Log("source is loading img: " + (source != null && source.ImageFile == StoragePath.LoadingImgPath)); Logger.Log("target style item is null: " + (targetStyleItem == null)); Logger.Log("target style item tooltip is null: " + (targetStyleItem != null && targetStyleItem.Tooltip == null)); Logger.Log("content slide is null: " + (contentSlide == null)); return !(source == null || source.ImageFile == StoragePath.LoadingImgPath || targetStyleItem == null || targetStyleItem.Tooltip == null || contentSlide == null); } private void UpdateStylesVariationImages(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight, bool isMockPreviewImages = false, int selectedId = -1) { Logger.Log("UpdateStylesVariationImages begins"); var copiedPicture = LoadClipboardPicture(); try { if (isMockPreviewImages) { Logger.Log("Generate mock images for Picture aspect"); } Logger.Log("Number of styles: " + _styleOptions.Count); if (selectedId != -1) { StylesVariationList[selectedId] = GenerateImageItem(source, contentSlide, slideWidth, slideHeight, isMockPreviewImages, selectedId); } else { for (var i = 0; i < _styleOptions.Count; i++) { StylesVariationList.Add( GenerateImageItem(source, contentSlide, slideWidth, slideHeight, isMockPreviewImages, i)); } } GC.Collect(); } catch (Exception e) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorImageCorrupted, e); Logger.LogException(e, "UpdateStylesVariationImages"); } SaveClipboardPicture(copiedPicture); Logger.Log("UpdateStylesVariationImages done"); } private ImageItem GenerateImageItem(ImageItem source, Slide contentSlide, float slideWidth, float slideHeight, bool isMockPreviewImages, int index) { var styleOption = _styleOptions[index]; PreviewInfo previewInfo; if (isMockPreviewImages) { previewInfo = new PreviewInfo { PreviewApplyStyleImagePath = StoragePath.NoPicturePlaceholderImgPath }; } else { previewInfo = Designer.PreviewApplyStyle( IsInPictureVariation() ? _8PicturesInPictureVariation[index] : source, contentSlide, slideWidth, slideHeight, styleOption); } return new ImageItem { ImageFile = previewInfo.PreviewApplyStyleImagePath, Tooltip = styleOption.OptionName }; } private ImageItem CreateChoosePicturesItem() { return new ImageItem { ImageFile = StoragePath.ChoosePicturesImgPath, Tooltip = "Choose pictures from local storage." }; } private static ImageItem CreateSamplePic2Item() { return new ImageItem { ImageFile = ImageUtil.GetThumbnailFromFullSizeImg( StoragePath.SampleImg2Path), FullSizeImageFile = StoragePath.SampleImg2Path, Tooltip = "Picture taken from Gary Elsasser https://flic.kr/p/5s5APp", ContextLink = "https://flic.kr/p/5s5APp", Source = "https://flic.kr/p/5s5APp" }; } private static ImageItem CreateSamplePic1Item() { return new ImageItem { ImageFile = ImageUtil.GetThumbnailFromFullSizeImg( StoragePath.SampleImg1Path), FullSizeImageFile = StoragePath.SampleImg1Path, Tooltip = "Picture taken from Alosh Bennett https://flic.kr/p/5fKBTq", ContextLink = "https://flic.kr/p/5fKBTq", Source = "https://flic.kr/p/5fKBTq" }; } #endregion #region Private Lifecycle private void InitFontFamilies() { FontFamilies = new ObservableCollection<string>(); foreach (var fontFamily in Fonts.SystemFontFamilies) { FontFamilies.Add(fontFamily.Source); } // add font family not in Fonts.SystemFontFamilies FontFamilies.Add("Segoe UI Light"); FontFamilies.Add("Calibri Light"); FontFamilies.Add("Arial Black"); FontFamilies.Add("Times New Roman Italic"); FontFamilies = new ObservableCollection<string>(FontFamilies.OrderBy(i => i)); } private void CleanUnusedPersistentData() { var imageFilesInUse = new HashSet<string>(); foreach (var imageItem in ImageSelectionList) { imageFilesInUse.Add(imageItem.ImageFile); imageFilesInUse.Add(imageItem.FullSizeImageFile); if (imageItem.CroppedImageFile != null) { imageFilesInUse.Add(imageItem.CroppedImageFile); imageFilesInUse.Add(imageItem.CroppedThumbnailImageFile); } } StoragePath.CleanPersistentFolder(imageFilesInUse); } private void InitUiModels() { StylesVariationList = new ObservableCollection<ImageItem>(); StylesVariationListSelectedId = new ObservableInt { Number = -1 }; StylesVariationListSelectedItem = new ObservableImageItem(); CurrentVariantCategory = new ObservableString(); CurrentVariantCategoryId = new ObservableInt { Number = -1 }; VariantsCategory = new ObservableCollection<string>(); SelectedFontId = new ObservableInt(); SelectedFontFamily = new ObservableFont(); SelectedSliderValue = new ObservableInt(); IsSliderValueChanged = new ObservableBoolean { Flag = false }; SelectedSliderMaximum = new ObservableInt(); SelectedSliderTickFrequency = new ObservableInt(); StylesPreviewList = new ObservableCollection<ImageItem>(); StylesPreviewListSelectedId = new ObservableInt { Number = -1 }; StylesPreviewListSelectedItem = new ObservableImageItem(); ImageSelectionList = new ObservableCollection<ImageItem>(); ImageSelectionList.Add(CreateChoosePicturesItem()); Settings = StoragePath.LoadSettings(); if (StoragePath.IsFirstTimeUsage()) { Logger.Log("First time use PSL"); ImageSelectionList.Add(CreateSamplePic1Item()); ImageSelectionList.Add(CreateSamplePic2Item()); } else { var loadedImageSelectionList = StoragePath.LoadPictures(); foreach (var item in loadedImageSelectionList) { if (item.FullSizeImageFile == null && item.BackupFullSizeImageFile != null) { item.FullSizeImageFile = item.BackupFullSizeImageFile; } else if (item.FullSizeImageFile == null && item.BackupFullSizeImageFile == null) { Logger.Log("Corrupted picture found. To be removed"); continue; } ImageSelectionList.Add(item); } } ImageSelectionListSelectedId = new ObservableInt { Number = -1 }; ImageSelectionListSelectedItem = new ObservableImageItem(); IsActiveDownloadProgressRing = new ObservableBoolean { Flag = false }; } private void InitStorage() { var isTempPathInit = Util.TempPath.InitTempFolder(); var isStoragePathInit = StoragePath.InitPersistentFolder(); if (!isTempPathInit || !isStoragePathInit) { View.ShowErrorMessageBox(PictureSlidesLabText.ErrorFailToInitTempFolder); Logger.Log("Failed to init storage"); } Logger.Log("Init storage done"); } #endregion } }