Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions CompactGUI/Services/WindowService.vb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ Public Class WindowService
.Content = String.Format(LanguageHelper.GetString("CompressionStop_Message"), folderName),
.DialogWidth = 640,
.PrimaryButtonText = LanguageHelper.GetString("CompressionStop_SaveProgress"),
.SecondaryButtonText = LanguageHelper.GetString("CompressionStop_UndoProgress"),
.CloseButtonText = LanguageHelper.GetString("CompressionStop_LeaveAsIs")
}

Expand All @@ -87,8 +86,6 @@ Public Class WindowService
Select Case Await dialog.ShowAsync()
Case Wpf.Ui.Controls.ContentDialogResult.Primary
Return CompressionStopChoice.SaveProgress
Case Wpf.Ui.Controls.ContentDialogResult.Secondary
Return CompressionStopChoice.UndoProgress
Case Else
Return If(cancelRequested, CompressionStopChoice.Cancel, CompressionStopChoice.LeaveAsIs)
End Select
Expand Down
76 changes: 62 additions & 14 deletions CompactGUI/ViewModels/HomeViewModel.vb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
Private ReadOnly _settingsService As ISettingsService
Private ReadOnly _compressableFolderService As CompressableFolderService
Private ReadOnly _folderValidationService As FolderValidationService
Private _stopBatchAfterCurrent As Boolean = False
Private _compressionBatchCompletion As TaskCompletionSource(Of Boolean)

Sub New(watcher As Watcher.Watcher, snackbarService As CustomSnackBarService, logger As ILogger(Of HomeViewModel), settingsService As ISettingsService, compressableFolderService As CompressableFolderService, folderValidationService As FolderValidationService)
WeakReferenceMessenger.Default.Register(Of WatcherAddedFolderToQueueMessage)(Me)
Expand Down Expand Up @@ -125,7 +127,6 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
Next

Dim validFolders = requestedFolders.Except(invalidFolderPaths, StringComparer.OrdinalIgnoreCase)
Dim foldersToResume As New Dictionary(Of CompressableFolder, SavedCompressionSession)()
Dim resumeService = Application.GetService(Of CompressionResumeService)()
Dim windowService = Application.GetService(Of IWindowService)()

Expand Down Expand Up @@ -158,10 +159,7 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
newFolder.CompressionOptions.SkipUserSubmittedFiletypes = _settingsService.AppSettings.SkipUserNonCompressable

If resumeChoice.HasValue AndAlso resumeChoice.Value = CompressionResumeChoice.ResumeProgress AndAlso savedSession IsNot Nothing Then
newFolder.CompressionOptions.SelectedCompressionMode = savedSession.SelectedCompressionMode
newFolder.CompressionOptions.SkipPoorlyCompressedFileTypes = savedSession.SkipPoorlyCompressedFileTypes
newFolder.CompressionOptions.SkipUserSubmittedFiletypes = savedSession.SkipUserSubmittedFiletypes
newFolder.CompressionOptions.WatchFolderForChanges = savedSession.WatchFolderForChanges
ApplyResumeSessionOptions(newFolder, savedSession)
End If

Folders.Add(newFolder)
Expand All @@ -186,14 +184,22 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
End If

If resumeChoice.HasValue AndAlso resumeChoice.Value = CompressionResumeChoice.ResumeProgress AndAlso savedSession IsNot Nothing Then
foldersToResume(newFolder) = savedSession
'Resume only prepares this folder to continue from its saved checkpoint.
'Actual compression starts when the user clicks Compress Selected.
newFolder.FolderActionState = ActionState.Idle
End If
Next

If foldersToResume.Count > 0 Then Await CompressFoldersAsync(foldersToResume.Keys.ToList(), foldersToResume)
End Function


Private Shared Sub ApplyResumeSessionOptions(folder As CompressableFolder, session As SavedCompressionSession)
folder.CompressionOptions.SelectedCompressionMode = session.SelectedCompressionMode
folder.CompressionOptions.SkipPoorlyCompressedFileTypes = session.SkipPoorlyCompressedFileTypes
folder.CompressionOptions.SkipUserSubmittedFiletypes = session.SkipUserSubmittedFiletypes
folder.CompressionOptions.WatchFolderForChanges = session.WatchFolderForChanges
End Sub


<RelayCommand>
Public Sub RemoveFolder(folder As CompressableFolder)
If Not CanRemoveFolder() Then
Expand Down Expand Up @@ -255,6 +261,25 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
Private _Compressing As Boolean = False


Public Function GetActiveManualCompressionFolder() As CompressableFolder
Return Folders.FirstOrDefault(
Function(folder)
Return TypeOf folder.Compressor Is Core.Compactor AndAlso
(folder.FolderActionState = ActionState.Working OrElse folder.FolderActionState = ActionState.Paused)
End Function)
End Function


Public Async Function StopManualCompressionForExitAsync(folder As CompressableFolder, choice As CompressionStopChoice?) As Task
_stopBatchAfterCurrent = True
Dim completion = _compressionBatchCompletion

If folder IsNot Nothing AndAlso choice.HasValue Then
_compressableFolderService.RequestCompressionStop(folder, choice.Value)
End If

If completion IsNot Nothing Then Await completion.Task
End Function


<RelayCommand>
Expand All @@ -263,16 +288,20 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
Await CompressFoldersAsync(foldersToCompress)
End Function

Private Async Function CompressFoldersAsync(
foldersToCompress As IReadOnlyCollection(Of CompressableFolder),
Optional resumeSessions As IReadOnlyDictionary(Of CompressableFolder, SavedCompressionSession) = Nothing) As Task
Private Async Function CompressFoldersAsync(foldersToCompress As IReadOnlyCollection(Of CompressableFolder)) As Task
If foldersToCompress Is Nothing OrElse foldersToCompress.Count = 0 Then Return

Await _watcher.DisableBackgrounding()

Dim completion = New TaskCompletionSource(Of Boolean)(TaskCreationOptions.RunContinuationsAsynchronously)
_compressionBatchCompletion = completion
_stopBatchAfterCurrent = False

Compressing = True
Core.SharedMethods.PreventSleep()
HomeViewModelLog.StartingBatchCompression(_logger, foldersToCompress.Count)

Dim resumeService = Application.GetService(Of CompressionResumeService)()
Dim watcherTargetOverrides As New Dictionary(Of CompressableFolder, Core.WOFCompressionAlgorithm)()
Dim foldersWithCompressionWork As New HashSet(Of CompressableFolder)()
Dim capturedException As Exception = Nothing
Expand All @@ -287,11 +316,19 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
'Keep the orchestration on the WPF synchronization context. The compressor performs
'its own asynchronous file work, while folder state and commands remain UI-thread-owned.
HomeViewModelLog.CompressingFolder(_logger, folder.FolderName)

Dim resumeSession As SavedCompressionSession = Nothing
If resumeSessions IsNot Nothing Then resumeSessions.TryGetValue(folder, resumeSession)
If resumeService.TryGetSession(folder.FolderName, resumeSession) Then
'A saved checkpoint belongs to the folder, not to the act of re-adding it.
'This also covers Compress Again while the folder remains in the Home list.
ApplyResumeSessionOptions(folder, resumeSession)
End If

Dim runResult = Await _compressableFolderService.CompressFolder(folder, resumeSession)
If Not runResult.HadWork Then Continue For
If Not runResult.HadWork Then
If _stopBatchAfterCurrent Then Exit For
Continue For
End If

foldersWithCompressionWork.Add(folder)
Await _compressableFolderService.AnalyseFolderAsync(folder)
Expand All @@ -307,6 +344,8 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient

watcherTargetOverrides(folder) = watcherTarget
Await _watcher.UpdateWatched(folder.FolderName, folder.Analyser, runResult.Completed, targetCompressionLevel:=watcherTarget)

If _stopBatchAfterCurrent Then Exit For
Next

For Each folder In Folders.Where(Function(item) item.CompressionOptions.WatchFolderForChanges)
Expand All @@ -326,7 +365,16 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient
Core.SharedMethods.RestoreSleep()
End Try

Await _watcher.EnableBackgrounding()
Try
Await _watcher.EnableBackgrounding()
Finally
If Object.ReferenceEquals(_compressionBatchCompletion, completion) Then
_compressionBatchCompletion = Nothing
End If
_stopBatchAfterCurrent = False
completion.TrySetResult(True)
End Try

If capturedException IsNot Nothing Then
ExceptionDispatchInfo.Capture(capturedException).Throw()
End If
Expand Down
109 changes: 95 additions & 14 deletions CompactGUI/ViewModels/MainWindowViewModel.vb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Partial Public Class MainWindowViewModel : Inherits ObservableRecipient : Implem
Private ReadOnly _watcher As Watcher.Watcher
Private ReadOnly _windowService As IWindowService
Private ReadOnly _settingsService As ISettingsService
Private _allowClose As Boolean = False
Private _isExitInProgress As Boolean = False

Public Sub New(windowService As IWindowService, watcher As Watcher.Watcher, settingsService As ISettingsService)
_watcher = watcher
Expand All @@ -38,35 +40,114 @@ Partial Public Class MainWindowViewModel : Inherits ObservableRecipient : Implem

<RelayCommand>
Private Async Function NotifyIconExit() As Task
If _watcher.WatchedFolders.Count = 0 Then Application.Current.Shutdown()
Dim message As String = String.Format(LanguageHelper.GetString("MessageBox_ExitText"), _watcher.WatchedFolders.Count)
Dim confirmed = Await _windowService.ShowMessageBox(LanguageHelper.GetString("Title_CompactGUI"), message)
If Not confirmed Then Return
_watcher.WriteToFile()
Application.Current.Shutdown()
End Function
If _isExitInProgress Then Return
_isExitInProgress = True

Try
If _watcher.WatchedFolders.Count <> 0 Then
Dim message As String = String.Format(LanguageHelper.GetString("MessageBox_ExitText"), _watcher.WatchedFolders.Count)
Dim confirmed = Await _windowService.ShowMessageBox(LanguageHelper.GetString("Title_CompactGUI"), message)
If Not confirmed Then Return
End If

<RelayCommand>
Private Sub Closing(e As ComponentModel.CancelEventArgs)
If e Is Nothing Then Return
If Not Await PrepareManualCompressionForExitAsync() Then Return

If Keyboard.Modifiers = ModifierKeys.Shift Then
e.Cancel = False
If _watcher.WatchedFolders.Count <> 0 Then _watcher.WriteToFile()
_settingsService.SaveSettings()
_allowClose = True
Application.Current.Shutdown()
Finally
If Not _allowClose Then _isExitInProgress = False
End Try
End Function


Private Async Function PrepareManualCompressionForExitAsync() As Task(Of Boolean)
Dim homeViewModel = Application.GetService(Of HomeViewModel)()
If homeViewModel Is Nothing OrElse Not homeViewModel.Compressing Then Return True

Dim activeFolder = homeViewModel.GetActiveManualCompressionFolder()
If activeFolder Is Nothing Then
Await homeViewModel.StopManualCompressionForExitAsync(Nothing, Nothing)
Return True
End If

_windowService.ShowMainWindow()

Dim pausedForStopDialog = False
Dim compressionFinishedWhilePausing = False
If activeFolder.FolderActionState = ActionState.Working Then
Try
activeFolder.Compressor?.Pause()
activeFolder.FolderActionState = ActionState.Paused
pausedForStopDialog = True
Catch ex As OperationCanceledException
compressionFinishedWhilePausing = True
Catch ex As ObjectDisposedException
compressionFinishedWhilePausing = True
End Try
End If

If compressionFinishedWhilePausing Then
Await homeViewModel.StopManualCompressionForExitAsync(Nothing, Nothing)
Return True
End If

Dim choice = Await _windowService.ShowCompressionStopDialog(activeFolder.DisplayName)
If choice = CompressionStopChoice.Cancel Then
If pausedForStopDialog AndAlso activeFolder.FolderActionState = ActionState.Paused Then
Try
activeFolder.Compressor?.Resume()
activeFolder.FolderActionState = ActionState.Working
Catch ex As OperationCanceledException
'The compression finished while the stop dialog was open.
Catch ex As ObjectDisposedException
'The compression finished while the stop dialog was open.
End Try
End If
Return False
End If

Await homeViewModel.StopManualCompressionForExitAsync(activeFolder, choice)
Return True
End Function


<RelayCommand>
Private Async Function Closing(e As ComponentModel.CancelEventArgs) As Task
If e Is Nothing Then Return
If _allowClose Then
e.Cancel = False
Return
End If

If _watcher.WatchedFolders.Count <> 0 Then
Dim forceExit = Keyboard.Modifiers = ModifierKeys.Shift

If Not forceExit AndAlso _watcher.WatchedFolders.Count <> 0 Then
e.Cancel = True
_windowService.MinimizeMainWindow()
_watcher.WriteToFile()
_windowService.HideMainWindow()
Return
End If

End Sub
'This close would actually exit the application. Cancel it synchronously so an
'active manual compression run can use the normal stop/save/undo prompt first.
e.Cancel = True
If _isExitInProgress Then Return
_isExitInProgress = True

Try
If Not Await PrepareManualCompressionForExitAsync() Then Return

If _watcher.WatchedFolders.Count <> 0 Then _watcher.WriteToFile()
_settingsService.SaveSettings()
_allowClose = True
Application.Current.Shutdown()
Finally
If Not _allowClose Then _isExitInProgress = False
End Try
End Function


Public Sub Receive(message As PropertyChangedMessage(Of CompressableFolder)) Implements IRecipient(Of PropertyChangedMessage(Of CompressableFolder)).Receive
Expand Down
Loading