/// <inheritdoc/> public bool Save(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (document.IsDisposed) { return(true); } Logger.Info(CultureInfo.InvariantCulture, "Saving document \"{0}\".", document.GetName()); if (document.IsUntitled) { return(SaveAs(document)); } try { document.Save(); RememberRecentFile(document.Uri); UpdateCommands(); return(true); } catch (Exception exception) { Logger.Warn(exception, CultureInfo.InvariantCulture, "Could not save file {0}.", document.Uri); string message = Invariant($"Could not save file.\n\n{exception.Message}"); MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); return(false); } }
/// <inheritdoc/> public bool Close(Document document, bool force) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (document.IsDisposed) { return(true); } Debug.Assert(_documents.Contains(document), "Document already closed."); Logger.Info(CultureInfo.InvariantCulture, "Closing document \"{0}\".", document.GetName()); bool canClose = force || PromptSaveChanges(document); if (canClose) { if (!document.IsUntitled) { RememberRecentFile(document.Uri); } document.Dispose(); foreach (var viewModel in document.ViewModels.ToArray()) { var task = viewModel.Conductor?.DeactivateItemAsync(viewModel, true); Debug.Assert(task.IsCompleted, "DeactivateItem expected to be synchronous operation."); Debug.Assert(task.Result, "DeactivateItem failed."); } Debug.Assert(!document.ViewModels.Any(), "One or more view models found. All document view models expected to be closed."); Debug.Assert(Editor.Items.OfType <DocumentViewModel>().All(vm => vm.Document != document), "Open view model is still referencing the closed document."); return(true); } return(false); }
internal void StartFileWatcher(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } Logger.Debug(CultureInfo.InvariantCulture, "Starting file watcher for document \"{0}\".", document.GetName()); if (_fileWatchers.Exists(fw => fw.Document == document)) { Logger.Warn(CultureInfo.InvariantCulture, "File watcher for document \"{0}\" already exists.", document.GetName()); return; } try { Logger.Debug(CultureInfo.InvariantCulture, "Creating new file watcher for document \"{0}\".", document.GetName()); var fileWatcher = new FileWatcher(document); fileWatcher.Changed += OnFileChanged; _fileWatchers.Add(fileWatcher); } catch (Exception exception) { Logger.Error(exception, CultureInfo.InvariantCulture, "Unable to start file watcher for document \"{0}\".", document.GetName()); throw; } }
/// <summary> /// Resumes the <see cref="FileSystemWatcher"/> for a document. /// </summary> /// <param name="document">The document which is monitored.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> /// <seealso cref="StartFileWatcher"/> /// <seealso cref="StopFileWatcher"/> /// <seealso cref="SuspendFileWatcher"/> /// <seealso cref="ResumeFileWatcher"/> ///// <seealso cref="SuspendAllFileWatchers"/> ///// <seealso cref="ResumeAllFileWatchers"/> internal void ResumeFileWatcher(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var fileWatcher = _fileWatchers.Find(fw => fw.Document == document); if (fileWatcher != null) { Logger.Debug(CultureInfo.InvariantCulture, "Resuming file watcher for document \"{0}\".", document.GetName()); fileWatcher.Resume(); } }
/// <summary> /// Stops the <see cref="FileSystemWatcher"/> for a document. /// </summary> /// <param name="document">The document which is monitored.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> /// <seealso cref="StartFileWatcher"/> /// <seealso cref="SuspendFileWatcher"/> /// <seealso cref="ResumeFileWatcher"/> ///// <seealso cref="SuspendAllFileWatchers"/> ///// <seealso cref="ResumeAllFileWatchers"/> internal void StopFileWatcher(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } Logger.Debug(CultureInfo.InvariantCulture, "Stopping file watcher for document \"{0}\".", document.GetName()); var fileWatcher = _fileWatchers.Find(fw => fw.Document == document); if (fileWatcher == null) { Logger.Warn(CultureInfo.InvariantCulture, "No file watcher found for document \"{0}\".", document.GetName()); return; } Logger.Debug(CultureInfo.InvariantCulture, "Stopping file watcher for document \"{0}\".", document.GetName()); fileWatcher.Dispose(); _fileWatchers.Remove(fileWatcher); // Remove any pending file changes for this document. lock (_fileChangeRecords) { _fileChangeRecords.RemoveAll(r => r.Document == document); } }
/// <summary> /// Suspends the <see cref="FileSystemWatcher"/> for a document. /// </summary> /// <param name="document">The document which is monitored.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> /// <seealso cref="StartFileWatcher"/> /// <seealso cref="StopFileWatcher"/> /// <seealso cref="SuspendFileWatcher"/> /// <seealso cref="ResumeFileWatcher"/> ///// <seealso cref="SuspendAllFileWatchers"/> ///// <seealso cref="ResumeAllFileWatchers"/> internal void SuspendFileWatcher(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); var fileWatcher = _fileWatchers.Find(fw => fw.Document == document); if (fileWatcher != null) { Logger.Debug(CultureInfo.InvariantCulture, "Suspending file watcher for document \"{0}\".", document.GetName()); fileWatcher.Suspend(); } }
/// <summary> /// Stops the <see cref="FileSystemWatcher"/> for a document. /// </summary> /// <param name="document">The document which is monitored.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> /// <seealso cref="StartFileWatcher"/> /// <seealso cref="SuspendFileWatcher"/> /// <seealso cref="ResumeFileWatcher"/> ///// <seealso cref="SuspendAllFileWatchers"/> ///// <seealso cref="ResumeAllFileWatchers"/> internal void StopFileWatcher(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); Logger.Debug(CultureInfo.InvariantCulture, "Stopping file watcher for document \"{0}\".", document.GetName()); var fileWatcher = _fileWatchers.Find(fw => fw.Document == document); if (fileWatcher == null) { Logger.Warn(CultureInfo.InvariantCulture, "No file watcher found for document \"{0}\".", document.GetName()); return; } Logger.Debug(CultureInfo.InvariantCulture, "Stopping file watcher for document \"{0}\".", document.GetName()); fileWatcher.Dispose(); _fileWatchers.Remove(fileWatcher); // Remove any pending file changes for this document. lock (_fileChangeRecords) { _fileChangeRecords.RemoveAll(r => r.Document == document); } }
internal void StartFileWatcher(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); Logger.Debug(CultureInfo.InvariantCulture, "Starting file watcher for document \"{0}\".", document.GetName()); if (_fileWatchers.Exists(fw => fw.Document == document)) { Logger.Warn(CultureInfo.InvariantCulture, "File watcher for document \"{0}\" already exists.", document.GetName()); return; } try { Logger.Debug(CultureInfo.InvariantCulture, "Creating new file watcher for document \"{0}\".", document.GetName()); var fileWatcher = new FileWatcher(document); fileWatcher.Changed += OnFileChanged; _fileWatchers.Add(fileWatcher); } catch (Exception exception) { Logger.Error(exception, CultureInfo.InvariantCulture, "Unable to start file watcher for document \"{0}\".", document.GetName()); throw; } }
private async Task<bool> CloseAllDocumentsButAsync(Document excludedDocument) { if (excludedDocument == null) Logger.Info("Closing all documents."); else Logger.Info(CultureInfo.InvariantCulture, "Closing all documents except: \"{0}\".", excludedDocument.GetName()); // Collect all documents that need to be saved. var modifiedDocuments = _documents.Where(document => document != excludedDocument && document.IsModified && document.DocumentType.IsSavable) .ToArray(); // Do we need to save the documents before closing them? if (modifiedDocuments.Length > 0) { var saveChangesDialog = new SaveChangesViewModel { ModifiedDocuments = modifiedDocuments, DisplayName = Editor.ApplicationName, }; _windowService.ShowDialog(saveChangesDialog); if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.SaveAndClose) { foreach (var document in modifiedDocuments) { bool success = Save(document); if (!success) { // The save operation failed or was canceled. --> Abort! Logger.Info("Save operation failed or was canceled by user. Canceling close operation"); return false; } } } else if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.CloseWithoutSaving) { Logger.Info("Discarding changes of remaining document."); } else { Debug.Assert(saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.Cancel); Logger.Info("Close operation canceled by user."); return false; } } // Close all documents foreach (var document in _documents.ToArray()) { if (document != excludedDocument) { Close(document, true); // Redraw GUI and keep app responsive. await Dispatcher.Yield(); } } return true; }
/// <inheritdoc/> public bool Close(Document document, bool force) { if (document == null) throw new ArgumentNullException(nameof(document)); if (document.IsDisposed) return true; Debug.Assert(_documents.Contains(document), "Document already closed."); Logger.Info(CultureInfo.InvariantCulture, "Closing document \"{0}\".", document.GetName()); bool canClose = force || PromptSaveChanges(document); if (canClose) { if (!document.IsUntitled) RememberRecentFile(document.Uri); document.Dispose(); foreach (var viewModel in document.ViewModels.ToArray()) { var task = viewModel.Conductor?.DeactivateItemAsync(viewModel, true); Debug.Assert(task.IsCompleted, "DeactivateItem expected to be synchronous operation."); Debug.Assert(task.Result, "DeactivateItem failed."); } Debug.Assert(!document.ViewModels.Any(), "One or more view models found. All document view models expected to be closed."); Debug.Assert(Editor.Items.OfType<DocumentViewModel>().All(vm => vm.Document != document), "Open view model is still referencing the closed document."); return true; } return false; }
/// <summary> /// Shows the "Save Changes" dialog for a document that is about to be closed and saves the /// document if required. /// </summary> /// <param name="document">The document.</param> /// <returns> /// <see langword="true"/> if all changes are saved or can be discarded; otherwise /// <see langword="false"/> if there are still changes that need to be saved. /// </returns> /// <remarks> /// This method checks if the document is modified and can be saved. If this is the case a /// dialog is displayed that tells the user that the document is about to close and asks if /// any changes should be saved or discarded, or if any close operation should be canceled. /// If necessary, <see cref="Save"/> is called automatically. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> public bool PromptSaveChanges(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); if (!document.IsModified || !document.DocumentType.IsSavable) return true; // Ask the user. var saveChangesDialog = new SaveChangesViewModel { ModifiedDocuments = new[] { document }, DisplayName = Editor.ApplicationName, }; _windowService.ShowDialog(saveChangesDialog); if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.SaveAndClose) { Logger.Info(CultureInfo.InvariantCulture, "Saving document \"{0}\".", document.GetName()); return Save(document); } if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.CloseWithoutSaving) { Logger.Info(CultureInfo.InvariantCulture, "Discarding changes of document \"{0}\".", document.GetName()); return true; } Debug.Assert(saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.Cancel); Logger.Info(CultureInfo.InvariantCulture, "Closing of document canceled by user."); return false; }
/// <summary> /// Saves the specified document to a file that the user can select from a "Save File" /// dialog. /// </summary> /// <returns> /// <see langword="true"/> if all changes were saved successfully or can be discarded; /// otherwise <see langword="false"/> if there are still changes that need to be saved. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> private bool SaveAs(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); Logger.Debug(CultureInfo.InvariantCulture, "Saving document \"{0}\" using the Save File dialog.", document.GetName()); var saveFileDialog = SaveFileDialog; if (document.IsUntitled) { saveFileDialog.FileName = document.UntitledName; } else { string path = document.Uri.LocalPath; string fileName = Path.GetFileName(path); string directory = Path.GetDirectoryName(path); saveFileDialog.FileName = fileName; saveFileDialog.InitialDirectory = directory; } saveFileDialog.Filter = document.FileDialogFilter; saveFileDialog.FilterIndex = document.FileDialogFilterIndex; bool? result = saveFileDialog.ShowDialog(); if (result == true) { try { document.Save(new Uri(saveFileDialog.FileName)); RememberRecentFile(document.Uri); UpdateCommands(); return true; } catch (Exception exception) { Logger.Warn(exception, CultureInfo.InvariantCulture, "Could not save file as \"{0}\".", saveFileDialog.FileName); string message = Invariant($"Could not save file.\n\n{exception.Message}"); MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } return false; }
/// <inheritdoc/> public bool Save(Document document) { if (document == null) throw new ArgumentNullException(nameof(document)); if (document.IsDisposed) return true; Logger.Info(CultureInfo.InvariantCulture, "Saving document \"{0}\".", document.GetName()); if (document.IsUntitled) return SaveAs(document); try { document.Save(); RememberRecentFile(document.Uri); UpdateCommands(); return true; } catch (Exception exception) { Logger.Warn(exception, CultureInfo.InvariantCulture, "Could not save file {0}.", document.Uri); string message = Invariant($"Could not save file.\n\n{exception.Message}"); MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); return false; } }
private async Task <bool> CloseAllDocumentsButAsync(Document excludedDocument) { if (excludedDocument == null) { Logger.Info("Closing all documents."); } else { Logger.Info(CultureInfo.InvariantCulture, "Closing all documents except: \"{0}\".", excludedDocument.GetName()); } // Collect all documents that need to be saved. var modifiedDocuments = _documents.Where(document => document != excludedDocument && document.IsModified && document.DocumentType.IsSavable) .ToArray(); // Do we need to save the documents before closing them? if (modifiedDocuments.Length > 0) { var saveChangesDialog = new SaveChangesViewModel { ModifiedDocuments = modifiedDocuments, DisplayName = Editor.ApplicationName, }; _windowService.ShowDialog(saveChangesDialog); if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.SaveAndClose) { foreach (var document in modifiedDocuments) { bool success = Save(document); if (!success) { // The save operation failed or was canceled. --> Abort! Logger.Info("Save operation failed or was canceled by user. Canceling close operation"); return(false); } } } else if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.CloseWithoutSaving) { Logger.Info("Discarding changes of remaining document."); } else { Debug.Assert(saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.Cancel); Logger.Info("Close operation canceled by user."); return(false); } } // Close all documents foreach (var document in _documents.ToArray()) { if (document != excludedDocument) { Close(document, true); // Redraw GUI and keep app responsive. await Dispatcher.Yield(); } } return(true); }
/// <summary> /// Shows the "Save Changes" dialog for a document that is about to be closed and saves the /// document if required. /// </summary> /// <param name="document">The document.</param> /// <returns> /// <see langword="true"/> if all changes are saved or can be discarded; otherwise /// <see langword="false"/> if there are still changes that need to be saved. /// </returns> /// <remarks> /// This method checks if the document is modified and can be saved. If this is the case a /// dialog is displayed that tells the user that the document is about to close and asks if /// any changes should be saved or discarded, or if any close operation should be canceled. /// If necessary, <see cref="Save"/> is called automatically. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> public bool PromptSaveChanges(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (!document.IsModified || !document.DocumentType.IsSavable) { return(true); } // Ask the user. var saveChangesDialog = new SaveChangesViewModel { ModifiedDocuments = new[] { document }, DisplayName = Editor.ApplicationName, }; _windowService.ShowDialog(saveChangesDialog); if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.SaveAndClose) { Logger.Info(CultureInfo.InvariantCulture, "Saving document \"{0}\".", document.GetName()); return(Save(document)); } if (saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.CloseWithoutSaving) { Logger.Info(CultureInfo.InvariantCulture, "Discarding changes of document \"{0}\".", document.GetName()); return(true); } Debug.Assert(saveChangesDialog.SaveChangesDialogResult == SaveChangesDialogResult.Cancel); Logger.Info(CultureInfo.InvariantCulture, "Closing of document canceled by user."); return(false); }
/// <summary> /// Saves the specified document to a file that the user can select from a "Save File" /// dialog. /// </summary> /// <returns> /// <see langword="true"/> if all changes were saved successfully or can be discarded; /// otherwise <see langword="false"/> if there are still changes that need to be saved. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> private bool SaveAs(Document document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } Logger.Debug(CultureInfo.InvariantCulture, "Saving document \"{0}\" using the Save File dialog.", document.GetName()); var saveFileDialog = SaveFileDialog; if (document.IsUntitled) { saveFileDialog.FileName = document.UntitledName; } else { string path = document.Uri.LocalPath; string fileName = Path.GetFileName(path); string directory = Path.GetDirectoryName(path); saveFileDialog.FileName = fileName; saveFileDialog.InitialDirectory = directory; } saveFileDialog.Filter = document.FileDialogFilter; saveFileDialog.FilterIndex = document.FileDialogFilterIndex; bool?result = saveFileDialog.ShowDialog(); if (result == true) { try { document.Save(new Uri(saveFileDialog.FileName)); RememberRecentFile(document.Uri); UpdateCommands(); return(true); } catch (Exception exception) { Logger.Warn(exception, CultureInfo.InvariantCulture, "Could not save file as \"{0}\".", saveFileDialog.FileName); string message = Invariant($"Could not save file.\n\n{exception.Message}"); MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } return(false); }