void OptimizeBeforeQueryStatus(object sender, bool lossy)
        {
            OleMenuCommand button = (OleMenuCommand)sender;

            _selectedPaths = ProjectHelpers.GetSelectedFilePaths().Where(file => Compressor.IsFileSupported(file)).ToList();

            int items = _selectedPaths.Count;

            var text = items == 1 ? " Optimize Image" : " Optimize Images";

            button.Text    = (lossy ? "Lossy" : "Lossless") + text;
            button.Visible = items > 0;
            button.Enabled = true;

            if (button.Visible && _isProcessing)
            {
                button.Enabled = false;
                button.Text   += " (running)";
            }
        }
        private void OptimizeImage(object sender, EventArgs e)
        {
            string text = _selectedPaths.Count == 1 ? " image" : " images";
            _dte.StatusBar.Progress(true, "Optimizing " + _selectedPaths.Count + text + "...", AmountCompleted: 0, Total: _selectedPaths.Count);

            Compressor compressor = new Compressor();
            List<CompressionResult> list = new List<CompressionResult>();

            System.Threading.ThreadPool.QueueUserWorkItem((o) =>
            {
                for (int i = 0; i < _selectedPaths.Count; i++)
                {
                    string file = _selectedPaths[i];

                    var result = compressor.CompressFile(file);
                    HandleResult(result, i + 1);

                    if (File.Exists(result.ResultFileName))
                        File.Delete(result.ResultFileName);

                    if (result.Saving > 0 && !string.IsNullOrEmpty(result.ResultFileName))
                        list.Add(result);
                }

                _dte.StatusBar.Progress(false);
                DisplayEndResult(list);
            });
        }
        private async Task OptimizeImageAsync(bool lossy, EventArgs e)
        {
            _isProcessing = true;

            IEnumerable <string> files = null;

            // Check command parameters first
            if (e is OleMenuCmdEventArgs cmdArgs && cmdArgs.InValue is string arg)
            {
                string filePath = arg.Trim('"', '\'');

                if (Compressor.IsFileSupported(filePath) && File.Exists(filePath))
                {
                    files = new[] { filePath };
                }
            }

            // Then check selected items
            if (files == null)
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                files = ProjectHelpers.GetSelectedFilePaths(_dte).Where(f => Compressor.IsFileSupported(f)).ToArray();
            }

            if (!files.Any())
            {
                _isProcessing = false;

                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                _dte.StatusBar.Text = "No images found to optimize";
                return;
            }

            var list      = new CompressionResult[files.Count()];
            var stopwatch = Stopwatch.StartNew();
            int count     = files.Count();

            await Task.Run(async() =>
            {
                try
                {
                    string text = count == 1 ? " image" : " images";
                    _dte.StatusBar.Progress(true, "Optimizing " + count + text + "...", AmountCompleted: 1, Total: count + 1);

                    var compressor = new Compressor();
                    var cache      = new Cache(_dte.Solution.FullName, lossy);
                    int nCompleted = 0;
                    var options    = new ParallelOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };

                    Parallel.For(0, count, options, i =>
                    {
                        string file = files.ElementAt(i);

                        // Don't process if file has been fully optimized already
                        if (cache.IsFullyOptimized(file))
                        {
                            var bogus = new CompressionResult(file, file, TimeSpan.Zero)
                            {
                                Processed = false
                            };
                            HandleResult(bogus, nCompleted + 1);
                        }
                        else
                        {
                            CompressionResult result = compressor.CompressFile(file, lossy);
                            HandleResult(result, nCompleted + 1);

                            if (result.Saving > 0 && result.ResultFileSize > 0 && !string.IsNullOrEmpty(result.ResultFileName))
                            {
                                list[i] = result;
                            }
                            else
                            {
                                cache.AddToCache(file);
                            }
                        }

                        Interlocked.Increment(ref nCompleted);
                    });
                }
                finally
                {
                    _dte.StatusBar.Progress(false);
                    stopwatch.Stop();
                    await DisplayEndResultAsync(list, stopwatch.Elapsed);
                    _isProcessing = false;
                }
            });
        }
        private async void OptimizeImageAsync(bool lossy)
        {
            _isProcessing = true;

            IEnumerable <string> files = ProjectHelpers.GetSelectedFilePaths(_dte).Where(f => Compressor.IsFileSupported(f));

            if (!files.Any())
            {
                _dte.StatusBar.Text = "No images found to optimize";
                _isProcessing       = false;
                return;
            }

            var list      = new CompressionResult[files.Count()];
            var stopwatch = Stopwatch.StartNew();
            int count     = files.Count();

            await Task.Run(async() =>
            {
                try
                {
                    string text = count == 1 ? " image" : " images";
                    _dte.StatusBar.Progress(true, "Optimizing " + count + text + "...", AmountCompleted: 1, Total: count + 1);

                    var compressor = new Compressor();
                    var cache      = new Cache(_dte.Solution, lossy);
                    int nCompleted = 0;
                    var options    = new ParallelOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };

                    Parallel.For(0, count, options, i =>
                    {
                        string file = files.ElementAt(i);

                        // Don't process if file has been fully optimized already
                        if (cache.IsFullyOptimized(file))
                        {
                            var bogus = new CompressionResult(file, file, TimeSpan.Zero)
                            {
                                Processed = false
                            };
                            HandleResult(bogus, nCompleted + 1);
                        }
                        else
                        {
                            CompressionResult result = compressor.CompressFileAsync(file, lossy);
                            HandleResult(result, nCompleted + 1);

                            if (result.Saving > 0 && result.ResultFileSize > 0 && !string.IsNullOrEmpty(result.ResultFileName))
                            {
                                list[i] = result;
                            }
                            else
                            {
                                cache.AddToCache(file);
                            }
                        }

                        Interlocked.Increment(ref nCompleted);
                    });
                }
                finally
                {
                    _dte.StatusBar.Progress(false);
                    stopwatch.Stop();
                    await DisplayEndResultAsync(list, stopwatch.Elapsed);
                    _isProcessing = false;
                }
            });
        }
        private async System.Threading.Tasks.Task OptimizeImage(object sender, EventArgs e)
        {
            string text = _selectedPaths.Count == 1 ? " image" : " images";
            _dte.StatusBar.Progress(true, "Optimizing " + _selectedPaths.Count + text + "...", AmountCompleted: 1, Total: _selectedPaths.Count + 1);

            Compressor compressor = new Compressor();
            List<CompressionResult> list = new List<CompressionResult>();

            for (int i = 0; i < _selectedPaths.Count; i++)
            {
                string file = _selectedPaths[i];

                var result = await compressor.CompressFileAsync(file);
                HandleResult(result, i + 1);

                if (File.Exists(result.ResultFileName))
                    File.Delete(result.ResultFileName);

                if (result.Saving > 0 && !string.IsNullOrEmpty(result.ResultFileName))
                {
                    list.Add(result);
                    Telemetry.TrackEvent(Path.GetExtension(file).ToLowerInvariant().Replace(".jpeg", ".jpg"));
                }
                else
                {
                    Telemetry.TrackEvent("Already optimized");
                }
            }

            _dte.StatusBar.Progress(false);
            DisplayEndResult(list);
        }