FileViewModel.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Media.Imaging;
  8. using Microsoft.Win32;
  9. using Newtonsoft.Json.Linq;
  10. using PixiEditor.Exceptions;
  11. using PixiEditor.Helpers;
  12. using PixiEditor.Models.DataHolders;
  13. using PixiEditor.Models.Dialogs;
  14. using PixiEditor.Models.IO;
  15. using PixiEditor.Models.UserPreferences;
  16. using PixiEditor.Parser;
  17. using PixiEditor.Views.Dialogs;
  18. namespace PixiEditor.ViewModels.SubViewModels.Main
  19. {
  20. public class FileViewModel : SubViewModel<ViewModelMain>
  21. {
  22. private bool hasRecent;
  23. public RelayCommand OpenNewFilePopupCommand { get; set; }
  24. public RelayCommand SaveDocumentCommand { get; set; }
  25. public RelayCommand OpenFileCommand { get; set; }
  26. public RelayCommand ExportFileCommand { get; set; } // Command that is used to save file
  27. public RelayCommand OpenRecentCommand { get; set; }
  28. public RelayCommand RemoveRecentlyOpenedCommand { get; set; }
  29. public bool HasRecent
  30. {
  31. get => hasRecent;
  32. set
  33. {
  34. hasRecent = value;
  35. RaisePropertyChanged(nameof(HasRecent));
  36. }
  37. }
  38. public RecentlyOpenedCollection RecentlyOpened { get; set; } = new RecentlyOpenedCollection();
  39. public FileViewModel(ViewModelMain owner)
  40. : base(owner)
  41. {
  42. OpenNewFilePopupCommand = new RelayCommand(OpenNewFilePopup);
  43. SaveDocumentCommand = new RelayCommand(SaveDocument, Owner.DocumentIsNotNull);
  44. OpenFileCommand = new RelayCommand(Open);
  45. ExportFileCommand = new RelayCommand(ExportFile, CanSave);
  46. OpenRecentCommand = new RelayCommand(OpenRecent);
  47. RemoveRecentlyOpenedCommand = new RelayCommand(RemoveRecentlyOpened);
  48. Owner.OnStartupEvent += Owner_OnStartupEvent;
  49. RecentlyOpened = new RecentlyOpenedCollection(GetRecentlyOpenedDocuments());
  50. if (RecentlyOpened.Count > 0)
  51. {
  52. HasRecent = true;
  53. }
  54. IPreferences.Current.AddCallback("MaxOpenedRecently", UpdateMaxRecentlyOpened);
  55. }
  56. public void OpenRecent(object parameter)
  57. {
  58. string path = (string)parameter;
  59. foreach (Document document in Owner.BitmapManager.Documents)
  60. {
  61. if (document.DocumentFilePath == path)
  62. {
  63. Owner.BitmapManager.ActiveDocument = document;
  64. return;
  65. }
  66. }
  67. if (!File.Exists(path))
  68. {
  69. NoticeDialog.Show("The file does no longer exist at that path");
  70. RecentlyOpened.Remove(path);
  71. return;
  72. }
  73. Open((string)parameter);
  74. }
  75. public void RemoveRecentlyOpened(object parameter)
  76. {
  77. if (RecentlyOpened.Contains((string)parameter))
  78. {
  79. RecentlyOpened.Remove((string)parameter);
  80. }
  81. }
  82. /// <summary>
  83. /// Generates new Layer and sets it as active one.
  84. /// </summary>
  85. /// <param name="parameter">CommandParameter.</param>
  86. public void OpenNewFilePopup(object parameter)
  87. {
  88. NewFileDialog newFile = new NewFileDialog();
  89. if (newFile.ShowDialog())
  90. {
  91. NewDocument(newFile.Width, newFile.Height);
  92. }
  93. }
  94. public void OpenHelloTherePopup()
  95. {
  96. new HelloTherePopup(this).Show();
  97. }
  98. public void NewDocument(int width, int height, bool addBaseLayer = true)
  99. {
  100. Owner.BitmapManager.Documents.Add(new Document(width, height));
  101. Owner.BitmapManager.ActiveDocument = Owner.BitmapManager.Documents[^1];
  102. if (addBaseLayer)
  103. {
  104. Owner.BitmapManager.ActiveDocument.AddNewLayer("Base Layer");
  105. }
  106. Owner.ResetProgramStateValues();
  107. }
  108. /// <summary>
  109. /// Opens file from path.
  110. /// </summary>
  111. /// <param name="path">Path to file.</param>
  112. public void OpenFile(string path)
  113. {
  114. ImportFileDialog dialog = new ImportFileDialog();
  115. if (path != null && File.Exists(path))
  116. {
  117. dialog.FilePath = path;
  118. }
  119. if (dialog.ShowDialog())
  120. {
  121. NewDocument(dialog.FileWidth, dialog.FileHeight, false);
  122. Owner.BitmapManager.ActiveDocument.DocumentFilePath = path;
  123. Owner.BitmapManager.ActiveDocument.AddNewLayer(
  124. "Image",
  125. Importer.ImportImage(dialog.FilePath, dialog.FileWidth, dialog.FileHeight));
  126. }
  127. }
  128. public void SaveDocument(bool asNew)
  129. {
  130. SaveDocument(parameter: asNew ? "asnew" : null);
  131. }
  132. public void OpenAny()
  133. {
  134. Open((object)null);
  135. }
  136. public void Open(string path)
  137. {
  138. try
  139. {
  140. if (path.EndsWith(".pixi"))
  141. {
  142. OpenDocument(path);
  143. }
  144. else
  145. {
  146. OpenFile(path);
  147. }
  148. Owner.ResetProgramStateValues();
  149. }
  150. catch (CorruptedFileException ex)
  151. {
  152. NoticeDialog.Show(ex.Message, "Failed to open file.");
  153. }
  154. catch (OldFileFormatException)
  155. {
  156. NoticeDialog.Show("This .pixi file uses the old format,\n which is no longer supported and can't be opened.", "Old file format");
  157. }
  158. }
  159. private void Owner_OnStartupEvent(object sender, System.EventArgs e)
  160. {
  161. var lastArg = Environment.GetCommandLineArgs().Last();
  162. if (Importer.IsSupportedFile(lastArg) && File.Exists(lastArg))
  163. {
  164. Open(lastArg);
  165. }
  166. else
  167. {
  168. if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
  169. {
  170. OpenHelloTherePopup();
  171. }
  172. }
  173. }
  174. private void Open(object property)
  175. {
  176. OpenFileDialog dialog = new OpenFileDialog
  177. {
  178. Filter =
  179. "Any|*.pixi;*.png;*.jpg;*.jpeg;|" +
  180. "PixiEditor Files | *.pixi|" +
  181. "Image Files|*.png;*.jpg;*.jpeg;",
  182. DefaultExt = "pixi"
  183. };
  184. if ((bool)dialog.ShowDialog())
  185. {
  186. if (Importer.IsSupportedFile(dialog.FileName))
  187. {
  188. Open(dialog.FileName);
  189. if (Owner.BitmapManager.Documents.Count > 0)
  190. {
  191. Owner.BitmapManager.ActiveDocument = Owner.BitmapManager.Documents.Last();
  192. }
  193. }
  194. }
  195. }
  196. private void OpenDocument(string path)
  197. {
  198. Document document = Importer.ImportDocument(path);
  199. if (Owner.BitmapManager.Documents.Select(x => x.DocumentFilePath).All(y => y != path))
  200. {
  201. Owner.BitmapManager.Documents.Add(document);
  202. Owner.BitmapManager.ActiveDocument = Owner.BitmapManager.Documents.Last();
  203. }
  204. else
  205. {
  206. Owner.BitmapManager.ActiveDocument = Owner.BitmapManager.Documents.First(y => y.DocumentFilePath == path);
  207. }
  208. }
  209. private void SaveDocument(object parameter)
  210. {
  211. bool paramIsAsNew = parameter != null && parameter.ToString()?.ToLower() == "asnew";
  212. if (paramIsAsNew ||
  213. string.IsNullOrEmpty(Owner.BitmapManager.ActiveDocument.DocumentFilePath) ||
  214. !Owner.BitmapManager.ActiveDocument.DocumentFilePath.EndsWith(".pixi"))
  215. {
  216. Owner.BitmapManager.ActiveDocument.SaveWithDialog();
  217. }
  218. else
  219. {
  220. Owner.BitmapManager.ActiveDocument.Save();
  221. }
  222. }
  223. /// <summary>
  224. /// Generates export dialog or saves directly if save data is known.
  225. /// </summary>
  226. /// <param name="parameter">CommandProperty.</param>
  227. private void ExportFile(object parameter)
  228. {
  229. WriteableBitmap bitmap = Owner.BitmapManager.GetCombinedLayersBitmap();
  230. Exporter.Export(bitmap, new Size(bitmap.PixelWidth, bitmap.PixelHeight));
  231. }
  232. /// <summary>
  233. /// Returns true if file save is possible.
  234. /// </summary>
  235. /// <param name="property">CommandProperty.</param>
  236. /// <returns>True if active document is not null.</returns>
  237. private bool CanSave(object property)
  238. {
  239. return Owner.BitmapManager.ActiveDocument != null;
  240. }
  241. private void UpdateMaxRecentlyOpened(object parameter)
  242. {
  243. int newAmount = (int)parameter;
  244. if (newAmount >= RecentlyOpened.Count)
  245. {
  246. return;
  247. }
  248. var recentlyOpeneds = new List<RecentlyOpenedDocument>(RecentlyOpened.Take(newAmount));
  249. RecentlyOpened.Clear();
  250. foreach (var recent in recentlyOpeneds)
  251. {
  252. RecentlyOpened.Add(recent);
  253. }
  254. }
  255. private List<RecentlyOpenedDocument> GetRecentlyOpenedDocuments()
  256. {
  257. var paths = IPreferences.Current.GetLocalPreference(nameof(RecentlyOpened), new JArray()).ToObject<string[]>()
  258. .Take(IPreferences.Current.GetPreference("MaxOpenedRecently", 8));
  259. List<RecentlyOpenedDocument> documents = new List<RecentlyOpenedDocument>();
  260. foreach (string path in paths)
  261. {
  262. documents.Add(new RecentlyOpenedDocument(path));
  263. }
  264. return documents;
  265. }
  266. }
  267. }