public FileWatcher(Document document) { // Check whether document is valid. if (document == null) throw new ArgumentNullException(nameof(document)); if (document.IsUntitled) throw new ArgumentException("Cannot monitor file. The specified document is untitled."); Document = document; // Check whether document points to a valid file. _fileName = document.Uri.LocalPath; if (string.IsNullOrEmpty(_fileName)) throw new ArgumentException("Cannot monitor file. The specified document does not contain a valid file URI."); Logger.Debug(CultureInfo.InvariantCulture, "Initializing FileWatcher for file \"{0}\".", _fileName); if (!File.Exists(_fileName)) Logger.Warn(CultureInfo.InvariantCulture, "Monitoring file \"{0}\". File not found.", _fileName); // Configure base FileSystemWatcher. Path = System.IO.Path.GetDirectoryName(_fileName); Filter = System.IO.Path.GetFileName(_fileName); NotifyFilter = NotifyFilters.LastWrite; EnableRaisingEvents = true; }
/// <summary> /// Initializes a new instance of the <see cref="FileChangeRecord"/> class. /// </summary> /// <param name="document">The affected document.</param> /// <param name="fileSystemEventArgs"> /// The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> or <paramref name="fileSystemEventArgs"/> are /// <see langword="null"/>. /// </exception> public FileChangeRecord(Document document, FileSystemEventArgs fileSystemEventArgs) { if (document == null) throw new ArgumentNullException(nameof(document)); if (fileSystemEventArgs == null) throw new ArgumentNullException(nameof(fileSystemEventArgs)); Document = document; FileSystemEventArgs = fileSystemEventArgs; }
//-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="DocumentViewModel"/> class. /// </summary> /// <param name="document">The document.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="document"/> is <see langword="null"/>. /// </exception> protected DocumentViewModel(Document document) { if (!WindowsHelper.IsInDesignMode) { if (document == null) throw new ArgumentNullException(nameof(document)); Document = document; DockId = Guid.NewGuid().ToString(); // Constant properties. DockContextMenu = document.DocumentExtension.DockContextMenu; Icon = document.DocumentType?.Icon; // Derived properties. UpdateProperties(); } }
/// <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; } }
/// <summary> /// Shows the document in the application. (Does not create a new view.) /// </summary> /// <param name="document">The document.</param> /// <remarks> /// If document has multiple views (dock windows), the first view is activated. /// </remarks> private static void ShowDocument(Document document) { foreach (var viewModel in document.ViewModels) { if (viewModel.Conductor != null) { var task = viewModel.Conductor.ActivateItemAsync(viewModel); Debug.Assert(task.IsCompleted, "ActivateItem expected to be synchronous operation."); if (task.Result) break; } } }
internal void UnregisterDocument(Document document) { Debug.Assert(document != null); Debug.Assert(_documents.Contains(document), "Document not registered in document service."); if (ActiveDocument == document) ActiveDocument = null; _documents.Remove(document); Debug.Assert(ActiveDocument == null || _documents.Contains(ActiveDocument), "Active document is not registered in document service."); }
internal void RegisterDocument(Document document) { Debug.Assert(document != null); Debug.Assert(!_documents.Contains(document), "Duplicate documents detected."); _documents.Add(document); Debug.Assert(ActiveDocument == null || _documents.Contains(ActiveDocument), "Active document is not registered in document service."); }
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 void Reload(Document document, bool forceReload) { if (document == null) throw new ArgumentNullException(nameof(document)); if (document.IsUntitled) return; if (document.IsModified && !forceReload) { var result = MessageBox.Show( "The document has been modified.\n\nDo you still want to reload the file and lose the changes made in the editor?", Editor.ApplicationName, MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); if (result == MessageBoxResult.No) return; } try { document.Load(document.Uri); } catch (Exception exception) { Logger.Warn(exception, CultureInfo.InvariantCulture, "Could not reload file {0}.", document.Uri); string message = Invariant($"Could not reload file.\n\n{exception.Message}"); MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } }
private void GoToLocation(Document document, int line, int column) { var vm = document.ViewModels.FirstOrDefault(); if (vm == null) return; Editor.ActivateItem(vm); var textDocumentVM = vm as TextDocumentViewModel; if (textDocumentVM == null) return; textDocumentVM.TextEditor.TextArea.Caret.Line = line; textDocumentVM.TextEditor.TextArea.Caret.Column = column; textDocumentVM.TextEditor.TextArea.Caret.BringCaretToView(); }