QuickImage – Diff between revs 7 and 8

Subversion Repositories:
Rev:
Show entire fileIgnore whitespace
Rev 7 Rev 8
Line 50... Line 50...
50 private readonly SemaphoreSlim _imageListViewLock; 50 private readonly SemaphoreSlim _imageListViewLock;
51 private readonly ImageTool _imageTool; 51 private readonly ImageTool _imageTool;
52 private readonly Progress<ImageListViewItemProgress<ListViewItem>> _listViewItemProgress; 52 private readonly Progress<ImageListViewItemProgress<ListViewItem>> _listViewItemProgress;
53 private readonly MagicMime _magicMime; 53 private readonly MagicMime _magicMime;
54 private readonly MD5 _md5; 54 private readonly MD5 _md5;
55 private readonly LogMemorySink _memorySink; 55 private readonly LogMemorySink _memorySink = new LogMemorySink();
56 private readonly QuickImageDatabase _quickImageDatabase; 56 private readonly QuickImageDatabase _quickImageDatabase;
Line 57... Line 57...
57   57  
58 private readonly Progress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>> 58 private readonly Progress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>>
Line 91... Line 91...
91 public enum MenuItemsToggleOperation 91 public enum MenuItemsToggleOperation
92 { 92 {
93 NONE, 93 NONE,
94 ENABLE, 94 ENABLE,
95 DISABLE 95 DISABLE
96 } 96 }
-   97  
-   98 private Configuration.Configuration Configuration { get; set; }
-   99  
-   100 public bool MemorySinkEnabled { get; set; } = true;
Line 97... Line 101...
97   101  
98 private Form1() 102 private Form1()
99 { 103 {
Line 123... Line 127...
123 _quickImageListViewProgress = 127 _quickImageListViewProgress =
124 new Progress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>>(); 128 new Progress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>>();
Line 125... Line 129...
125   129  
126 _tagAutoCompleteStringCollection = new AutoCompleteStringCollection(); 130 _tagAutoCompleteStringCollection = new AutoCompleteStringCollection();
127 textBox1.AutoCompleteCustomSource = _tagAutoCompleteStringCollection; 131 textBox1.AutoCompleteCustomSource = _tagAutoCompleteStringCollection;
128 tagTextBox.AutoCompleteCustomSource = _tagAutoCompleteStringCollection; 132 tagTextBox.AutoCompleteCustomSource = _tagAutoCompleteStringCollection;
-   133  
-   134 _quickImageDatabase = new QuickImageDatabase(_cancellationToken);
129   135 _tagManager = new TagManager(_fileMutex);
Line 130... Line 136...
130 _memorySink = new LogMemorySink(); 136 _imageTool = new ImageTool();
131   137  
132 Log.Logger = new LoggerConfiguration() 138 Log.Logger = new LoggerConfiguration()
133 .MinimumLevel.Debug() 139 .MinimumLevel.Debug()
Line 142... Line 148...
142   148  
143 _sparkle = new SparkleUpdater("https://quickimage.grimore.org/update/appcast.xml", 149 _sparkle = new SparkleUpdater("https://quickimage.grimore.org/update/appcast.xml",
144 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY=")) 150 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
145 { 151 {
146 UIFactory = new UIFactory(icon), 152 UIFactory = new UIFactory(icon),
147 RelaunchAfterUpdate = true, -  
148 //SecurityProtocolType = SecurityProtocolType.Tls12, -  
149 //ShowsUIOnMainThread = false, -  
150 LogWriter = new LogWriter(LogWriterOutputMode.None) 153 RelaunchAfterUpdate = true
151 }; -  
Line 152... Line -...
152 _sparkle.StartLoop(true, true); -  
153   -  
154 _quickImageDatabase = new QuickImageDatabase(_cancellationToken); 154 };
155 _tagManager = new TagManager(_fileMutex); 155  
Line 156... Line 156...
156 _imageTool = new ImageTool(); 156 _sparkle.StartLoop(true, true);
157 } 157 }
158   158  
Line 159... Line -...
159 public Form1(Mutex mutex) : this() -  
160 { -  
161 } -  
162   159 public Form1(Mutex mutex) : this()
163 private Configuration.Configuration Configuration { get; set; } 160 {
164 public bool MemorySinkEnabled { get; set; } -  
165   161 }
Line 166... Line 162...
166 private async Task SortImageListView(IComparer<ListViewItem> comparer) 162  
167 { 163 private async Task SortImageListView(IComparer<ListViewItem> comparer)
168 var taskCompletionSources = new[] 164 {
169 { new TaskCompletionSource<object>(), new TaskCompletionSource<object>() }; 165 var taskCompletionSources = new[] { new TaskCompletionSource<object>(), new TaskCompletionSource<object>() };
Line 227... Line 223...
227 finally 223 finally
228 { 224 {
229 this.InvokeIfRequired(form => 225 this.InvokeIfRequired(form =>
230 { 226 {
231 toolStripStatusLabel1.Text = "Sorting complete."; 227 toolStripStatusLabel1.Text = "Sorting complete.";
-   228  
232 form.toolStripProgressBar1.MarqueeAnimationSpeed = 0; 229 form.toolStripProgressBar1.MarqueeAnimationSpeed = 0;
233 }); 230 });
-   231  
234 _imageListViewLock.Release(); 232 _imageListViewLock.Release();
235 } 233 }
236 } 234 }
Line 237... Line 235...
237   235  
238 private async Task BalanceTags(IEnumerable<ListViewItem> items) 236 private async Task BalanceTags(IEnumerable<ListViewItem> items)
239 { 237 {
240 var enumerable = items as ListViewItem[] ?? items.ToArray(); 238 var enumerable = items as ListViewItem[] ?? items.ToArray();
Line 241... Line 239...
241 var count = enumerable.Length; 239 var count = enumerable.Length;
242   -  
243 var bufferBlock = new BufferBlock<(string Name, ConcurrentBag<string> Tags)>(new DataflowBlockOptions 240  
244 { CancellationToken = _cancellationToken }); -  
245 var broadcastBlock = new BroadcastBlock<(string Name, ConcurrentBag<string> Tags)>(x => x, 241 var bufferBlock = new BufferBlock<(string Name, ConcurrentBag<string> Tags)>(new DataflowBlockOptions{ CancellationToken = _cancellationToken });
246 new DataflowBlockOptions { CancellationToken = _cancellationToken }); 242 var broadcastBlock = new BroadcastBlock<(string Name, ConcurrentBag<string> Tags)>(x => x, new DataflowBlockOptions { CancellationToken = _cancellationToken });
247 var databaseActionBlock = new ActionBlock<(string Name, ConcurrentBag<string> Tags)>( 243 var databaseActionBlock = new ActionBlock<(string Name, ConcurrentBag<string> Tags)>(
248 async file => 244 async file =>
249 { -  
250 await Task.WhenAll(_quickImageDatabase.AddTagsAsync(file.Name, file.Tags, _cancellationToken), 245 {
251 _tagManager.AddIptcKeywords(file.Name, file.Tags, _cancellationToken)); 246 await Task.WhenAll(_quickImageDatabase.AddTagsAsync(file.Name, file.Tags, _cancellationToken), _tagManager.AddIptcKeywords(file.Name, file.Tags, _cancellationToken));
252 }, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); 247 }, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken });
253 var taggingActionBlock = new ActionBlock<(string Name, ConcurrentBag<string> Tags)>(file => 248 var taggingActionBlock = new ActionBlock<(string Name, ConcurrentBag<string> Tags)>(file =>
254 { 249 {
Line 283... Line 278...
283 try 278 try
284 { 279 {
285 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = "Balancing tags..."; }); 280 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = "Balancing tags..."; });
Line 286... Line 281...
286   281  
287 var tags = new ConcurrentBag<string>(); 282 var tags = new ConcurrentBag<string>();
288 foreach (var item in enumerable) 283 foreach (var item in enumerable)
289 await foreach (var tag in 284 {
290 _quickImageDatabase.GetTags(item.Name, _cancellationToken) 285 await foreach (var tag in _quickImageDatabase.GetTags(item.Name, _cancellationToken).WithCancellation(_cancellationToken))
291 .WithCancellation(_cancellationToken)) 286 {
-   287 tags.Add(tag);
-   288 }
Line 292... Line 289...
292 tags.Add(tag); 289 }
293   290  
-   291 var tasks = new List<Task>();
294 var tasks = new List<Task>(); 292 foreach (var item in enumerable)
-   293 {
Line 295... Line 294...
295 foreach (var item in enumerable) 294 tasks.Add(bufferBlock.SendAsync((item.Name, Tags: tags), _cancellationToken));
Line 296... Line 295...
296 tasks.Add(bufferBlock.SendAsync((item.Name, Tags: tags), _cancellationToken)); 295 }
-   296  
297   297 await Task.WhenAll(tasks);
298 await Task.WhenAll(tasks); 298  
299   299 bufferBlock.Complete();
300 bufferBlock.Complete(); 300  
301 await bufferBlock.Completion; 301 await bufferBlock.Completion;
Line 368... Line 368...
368 } 368 }
369 } 369 }
Line 370... Line 370...
370   370  
371 if (count == 0) 371 if (count == 0)
-   372 {
-   373 if (_selectionCancellationTokenSource != null)
372 { 374 {
-   375 _selectionCancellationTokenSource.Cancel();
Line 373... Line 376...
373 if (_selectionCancellationTokenSource != null) _selectionCancellationTokenSource.Cancel(); 376 }
374   377  
375 tagListView.BeginUpdate(); 378 tagListView.BeginUpdate();
376 foreach (var item in tagListView.CheckedItems.OfType<ListViewItem>()) 379 foreach (var item in tagListView.CheckedItems.OfType<ListViewItem>())
Line 380... Line 383...
380   383  
381 tagListView.EndUpdate(); 384 tagListView.EndUpdate();
382 return; 385 return;
Line -... Line 386...
-   386 }
-   387  
383 } 388 if (_selectionCancellationTokenSource != null)
-   389 {
Line 384... Line 390...
384   390 _selectionCancellationTokenSource.Cancel();
385 if (_selectionCancellationTokenSource != null) _selectionCancellationTokenSource.Cancel(); 391 }
386   392  
387 _selectionCancellationTokenSource = new CancellationTokenSource(); 393 _selectionCancellationTokenSource = new CancellationTokenSource();
Line 396... Line 402...
396 try 402 try
397 { 403 {
398 tagListView.InvokeIfRequired(view => 404 tagListView.InvokeIfRequired(view =>
399 { 405 {
400 view.BeginUpdate(); 406 view.BeginUpdate();
-   407  
401 foreach (var item in view.CheckedItems.OfType<ListViewItem>()) 408 foreach (var item in view.CheckedItems.OfType<ListViewItem>())
402 { 409 {
403 item.Checked = false; 410 item.Checked = false;
404 } 411 }
Line 410... Line 417...
410 { 417 {
411 form.toolStripStatusLabel1.Text = "Selecting items..."; 418 form.toolStripStatusLabel1.Text = "Selecting items...";
412 Application.Idle += IdleHandler; 419 Application.Idle += IdleHandler;
413 }); 420 });
Line 414... Line 421...
414   421  
415 var tasks = new List<Task>(); 422 var tasks = new ConcurrentBag<Task>();
416 foreach (var item in enumerable) 423 foreach (var item in enumerable)
417 { 424 {
418 await foreach (var tag in _quickImageDatabase.GetTags(item.Name, _combinedSelectionCancellationToken).WithCancellation(_combinedSelectionCancellationToken)) 425 await foreach (var tag in _quickImageDatabase.GetTags(item.Name, _combinedSelectionCancellationToken).WithCancellation(_combinedSelectionCancellationToken))
419 { 426 {
Line 435... Line 442...
435 Log.Warning(exception, "Could not select items."); 442 Log.Warning(exception, "Could not select items.");
436 } 443 }
437 }, _combinedSelectionCancellationToken); 444 }, _combinedSelectionCancellationToken);
438 } 445 }
Line 439... Line 446...
439   446  
440 private async Task RelocateToAsync(IEnumerable<ListViewItem> items, string destinationDirectory, 447 private void RelocateTo(IEnumerable<ListViewItem> items, string destinationDirectory,
441 CancellationToken cancellationToken) 448 CancellationToken cancellationToken)
442 { 449 {
Line 443... Line 450...
443 var enumerable = items as ListViewItem[] ?? items.ToArray(); 450 var enumerable = items as ListViewItem[] ?? items.ToArray();
Line 537... Line 544...
537 return; 544 return;
538 } 545 }
Line 539... Line 546...
539   546  
540 try 547 try
541 { -  
542 using var _1 = transformBlock.LinkTo(actionBlock, 548 {
Line -... Line 549...
-   549 using var _1 = transformBlock.LinkTo(actionBlock, new DataflowLinkOptions { PropagateCompletion = true });
543 new DataflowLinkOptions { PropagateCompletion = true }); 550  
544   551 var tasks = new ConcurrentBag<Task>();
545 foreach (var item in enumerable) 552 foreach (var item in enumerable)
-   553 {
-   554 if (cancellationToken.IsCancellationRequested)
-   555 {
Line 546... Line 556...
546 { 556 return;
-   557 }
547 if (cancellationToken.IsCancellationRequested) return; 558  
Line -... Line 559...
-   559 var task = transformBlock.SendAsync((Item: item, File: destinationDirectory), cancellationToken);
548   560 tasks.Add(task);
549 await transformBlock.SendAsync((Item: item, File: destinationDirectory), cancellationToken); 561 }
550 } 562  
551   563 await Task.WhenAll(tasks);
552 transformBlock.Complete(); 564 transformBlock.Complete();
553 await actionBlock.Completion; 565 await actionBlock.Completion;
554 } 566 }
555 finally 567 finally
556 { 568 {
Line 557... Line 569...
557 _imageListViewLock.Release(); 569 _imageListViewLock.Release();
558 } 570 }
559 }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); 571 }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
Line 560... Line 572...
560 } 572 }
561   573  
562 private async Task RemoveImagesAsync(IEnumerable<ListViewItem> items, CancellationToken cancellationToken) 574 private void RemoveImages(IEnumerable<ListViewItem> items, CancellationToken cancellationToken)
563 { 575 {
Line 564... Line 576...
564 var enumerable = items as ListViewItem[] ?? items.ToArray(); 576 var enumerable = items as ListViewItem[] ?? items.ToArray();
565   -  
-   577  
566 toolStripProgressBar1.Style = ProgressBarStyle.Continuous; 578 toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
567 toolStripProgressBar1.Minimum = 0; 579 toolStripProgressBar1.Minimum = 0;
568 toolStripProgressBar1.Maximum = enumerable.Length; 580 toolStripProgressBar1.Maximum = enumerable.Length;
569 toolStripProgressBar1.Value = 0; 581 toolStripProgressBar1.Value = 0;
570   582  
571 var bufferBlock = new BufferBlock<ListViewItem>(new DataflowBlockOptions 583 var bufferBlock = new BufferBlock<ListViewItem>(new DataflowBlockOptions { CancellationToken = cancellationToken });
572 { CancellationToken = cancellationToken }); -  
573 var actionBlock = new ActionBlock<ListViewItem>(listViewItem => 584  
Line 574... Line 585...
574 { 585 var actionBlock = new ActionBlock<ListViewItem>(listViewItem =>
Line 575... Line 586...
575 toolStripStatusLabel1.Text = $"Unloading image {listViewItem.Name}"; 586 {
576 imageListView.Items.Remove(listViewItem); 587 toolStripStatusLabel1.Text = $"Unloading image {listViewItem.Name}";
Line 594... Line 605...
594 return; 605 return;
595 } 606 }
Line 596... Line 607...
596   607  
597 try 608 try
598 { -  
599 using var _ = bufferBlock.LinkTo(actionBlock, 609 {
Line 600... Line 610...
600 new DataflowLinkOptions { PropagateCompletion = true }); 610 using var _ = bufferBlock.LinkTo(actionBlock, new DataflowLinkOptions { PropagateCompletion = true });
601   611  
602 foreach (var item in enumerable) 612 foreach (var item in enumerable)
Line 603... Line 613...
603 { 613 {
604 if (cancellationToken.IsCancellationRequested) return; 614 if (cancellationToken.IsCancellationRequested) return;
605   615  
-   616 try
-   617 {
-   618 if (!await _quickImageDatabase.RemoveImageAsync(item.Name, cancellationToken))
Line 606... Line 619...
606 try 619 {
607 { 620 continue;
608 if (!await _quickImageDatabase.RemoveImageAsync(item.Name, cancellationToken)) continue; 621 }
609   622  
Line 648... Line 661...
648 new ExecutionDataflowBlockOptions 661 new ExecutionDataflowBlockOptions
649 { CancellationToken = cancellationToken, TaskScheduler = _formTaskScheduler }); 662 { CancellationToken = cancellationToken, TaskScheduler = _formTaskScheduler });
Line 650... Line 663...
650   663  
651 var actionBlock = new ActionBlock<ListViewItem>(async item => 664 var actionBlock = new ActionBlock<ListViewItem>(async item =>
652 { 665 {
-   666 if (item == null)
-   667 {
-   668 return;
Line 653... Line 669...
653 if (item == null) return; 669 }
654   670  
655 try 671 try
656 { 672 {
Line 679... Line 695...
679 toolStripProgressBar1.Style = ProgressBarStyle.Marquee; 695 toolStripProgressBar1.Style = ProgressBarStyle.Marquee;
680 toolStripProgressBar1.MarqueeAnimationSpeed = 30; 696 toolStripProgressBar1.MarqueeAnimationSpeed = 30;
Line 681... Line 697...
681   697  
682 try 698 try
683 { 699 {
684 var tasks = new List<Task>(); 700 var tasks = new ConcurrentBag<Task>();
-   701 foreach (var group in enumerable)
685 foreach (var group in enumerable) 702 {
-   703 foreach (var item in imageListView.Items.OfType<ListViewItem>())
686 foreach (var item in imageListView.Items.OfType<ListViewItem>()) 704 {
-   705 if (string.Equals(item.Group.Name, group, StringComparison.OrdinalIgnoreCase))
687 if (string.Equals(item.Group.Name, group, StringComparison.OrdinalIgnoreCase)) 706 {
-   707 tasks.Add(bufferBlock.SendAsync(item, cancellationToken));
-   708 }
-   709 }
Line 688... Line 710...
688 tasks.Add(bufferBlock.SendAsync(item, cancellationToken)); 710 }
Line 689... Line 711...
689   711  
Line 719... Line 741...
719 { 741 {
720 toolStripStatusLabel1.Text = $"File {file} already exits."; 742 toolStripStatusLabel1.Text = $"File {file} already exits.";
721 return; 743 return;
722 } 744 }
Line -... Line 745...
-   745  
-   746 if (!largeImageList.Images.ContainsKey(file))
723   747 {
-   748 largeImageList.Images.Add(file, thumbnail);
Line 724... Line 749...
724 if (!largeImageList.Images.ContainsKey(file)) largeImageList.Images.Add(file, thumbnail); 749 }
725   750  
726 var fileInfo = new FileInfo(file); 751 var fileInfo = new FileInfo(file);
727 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group)) 752 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group))
728 { -  
729 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) 753 {
730 { Name = fileInfo.DirectoryName }; 754 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) { Name = fileInfo.DirectoryName };
731 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group); 755 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group);
Line 732... Line -...
732 imageListView.Groups.Add(group); -  
733 } 756 imageListView.Groups.Add(group);
734   757 }
735 var imageListViewItem = new ListViewItem(file) 758  
Line 736... Line 759...
736 { Name = file, ImageKey = file, Text = fileInfo.Name, Group = group }; 759 var imageListViewItem = new ListViewItem(file) { Name = file, ImageKey = file, Text = fileInfo.Name, Group = group };
Line 759... Line 782...
759 catch (Exception exception) 782 catch (Exception exception)
760 { 783 {
761 Log.Warning(exception, "Could not update image list view."); 784 Log.Warning(exception, "Could not update image list view.");
762 } 785 }
763 }, 786 },
764 new ExecutionDataflowBlockOptions -  
765 { CancellationToken = cancellationToken, TaskScheduler = _formTaskScheduler }); 787 new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken, TaskScheduler = _formTaskScheduler });
Line 766... Line 788...
766   788  
767 var fileInputBlock = new BufferBlock<string>(new ExecutionDataflowBlockOptions -  
Line 768... Line 789...
768 { CancellationToken = cancellationToken }); 789 var fileInputBlock = new BufferBlock<string>(new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken });
769   790  
770 var updateImageTagsTransformBlock = new TransformBlock<string, Database.QuickImage>(async file => 791 var updateImageTagsTransformBlock = new TransformBlock<string, Database.QuickImage>(async file =>
771 { 792 {
772 try 793 try
Line 773... Line 794...
773 { 794 {
774 var tags = new HashSet<string>(); -  
Line 775... Line 795...
775   795 var tags = new HashSet<string>();
Line 776... Line 796...
776 var databaseTags = await _quickImageDatabase.GetTags(file, cancellationToken) 796  
Line 813... Line 833...
813 var mime = await magicMime.GetMimeType(file, cancellationToken); 833 var mime = await magicMime.GetMimeType(file, cancellationToken);
Line 814... Line 834...
814   834  
815 if (Configuration.SupportedFormats.IsSupportedImage(mime)) 835 if (Configuration.SupportedFormats.IsSupportedImage(mime))
816 { 836 {
-   837 var iptcTags = _tagManager.GetIptcKeywords(imageFrame);
817 var iptcTags = _tagManager.GetIptcKeywords(imageFrame); 838  
818 tags = iptcTags.ToArray(); 839 tags = iptcTags.ToArray();
Line 819... Line 840...
819 } 840 }
820   841  
Line 857... Line 878...
857 using var _4 = updateImageTagsTransformBlock.LinkTo(updateImageListViewActionBlock, new DataflowLinkOptions { PropagateCompletion = true }); 878 using var _4 = updateImageTagsTransformBlock.LinkTo(updateImageListViewActionBlock, new DataflowLinkOptions { PropagateCompletion = true });
858 using var _5 = 879 using var _5 =
859 updateImageTagsTransformBlock.LinkTo(DataflowBlock.NullTarget<Database.QuickImage>(), image => 880 updateImageTagsTransformBlock.LinkTo(DataflowBlock.NullTarget<Database.QuickImage>(), image =>
860 { 881 {
861 var r = image == null; 882 var r = image == null;
-   883  
862 return r; 884 return r;
863 }); 885 });
Line 864... Line 886...
864   886  
865 using var _3 = fileInputBlock.LinkTo(createImageTransformBlock, new DataflowLinkOptions { PropagateCompletion = true }); 887 using var _3 = fileInputBlock.LinkTo(createImageTransformBlock, new DataflowLinkOptions { PropagateCompletion = true });
866 using var _6 = createImageTransformBlock.LinkTo(updateImageListViewActionBlock, new DataflowLinkOptions { PropagateCompletion = true }); 888 using var _6 = createImageTransformBlock.LinkTo(updateImageListViewActionBlock, new DataflowLinkOptions { PropagateCompletion = true });
867 using var _7 = 889 using var _7 =
868 createImageTransformBlock.LinkTo(DataflowBlock.NullTarget<Database.QuickImage>(), image => 890 createImageTransformBlock.LinkTo(DataflowBlock.NullTarget<Database.QuickImage>(), image =>
869 { 891 {
-   892 var r = image == null;
870 var r = image == null; 893  
871 return r; 894 return r;
Line 872... Line 895...
872 }); 895 });
873   896  
Line 884... Line 907...
884 try 907 try
885 { 908 {
886 toolStripProgressBar1.Style = ProgressBarStyle.Marquee; 909 toolStripProgressBar1.Style = ProgressBarStyle.Marquee;
887 toolStripProgressBar1.MarqueeAnimationSpeed = 30; 910 toolStripProgressBar1.MarqueeAnimationSpeed = 30;
Line 888... Line 911...
888   911  
889 var tasks = new List<Task>(); 912 var tasks = new ConcurrentBag<Task>();
890 foreach (var item in enumerable) 913 foreach (var item in enumerable)
891 { 914 {
892 await foreach (var entry in GetFilesAsync(item, Configuration, magicMime, cancellationToken).WithCancellation(cancellationToken)) 915 await foreach (var entry in GetFilesAsync(item, Configuration, magicMime, cancellationToken).WithCancellation(cancellationToken))
893 { 916 {
Line 903... Line 926...
903 } 926 }
904 finally 927 finally
905 { 928 {
906 toolStripProgressBar1.MarqueeAnimationSpeed = 0; 929 toolStripProgressBar1.MarqueeAnimationSpeed = 0;
907 _imageListViewLock.Release(); 930 _imageListViewLock.Release();
-   931  
908 toolStripStatusLabel1.Text = "Done loading images."; 932 toolStripStatusLabel1.Text = "Done loading images.";
909 } 933 }
910 } 934 }
Line 911... Line 935...
911   935  
Line 985... Line 1009...
985 toolStripProgressBar1.Style = ProgressBarStyle.Continuous; 1009 toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
986 toolStripProgressBar1.Minimum = 0; 1010 toolStripProgressBar1.Minimum = 0;
987 toolStripProgressBar1.Maximum = enumerable.Length; 1011 toolStripProgressBar1.Maximum = enumerable.Length;
988 toolStripProgressBar1.Value = 0; 1012 toolStripProgressBar1.Value = 0;
Line 989... Line -...
989   -  
990 void QuickImageListViewItemProgress(object sender, 1013  
991 ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e) 1014 void QuickImageListViewItemProgress(object sender, ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e)
992 { 1015 {
993 switch (e) 1016 switch (e)
994 { 1017 {
995 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)> -  
996 imageListViewItemProgressSuccess: 1018 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)> imageListViewItemProgressSuccess:
997 if (imageListViewItemProgressSuccess.Item is { } tuple) 1019 if (imageListViewItemProgressSuccess.Item is { } tuple)
998 { 1020 {
Line 999... Line 1021...
999 var (item, image) = tuple; 1021 var (item, image) = tuple;
Line 1012... Line 1034...
1012 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group); 1034 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group);
1013 imageListView.Groups.Add(group); 1035 imageListView.Groups.Add(group);
1014 } 1036 }
Line 1015... Line 1037...
1015   1037  
-   1038 largeImageList.Images.RemoveByKey(item.Name);
1016 largeImageList.Images.RemoveByKey(item.Name); 1039  
Line 1017... Line 1040...
1017 largeImageList.Images.Add(image.File, image.Thumbnail); 1040 largeImageList.Images.Add(image.File, image.Thumbnail);
1018   1041  
1019 imageListView.Items.Add(new ListViewItem(fileInfo.DirectoryName) 1042 imageListView.Items.Add(new ListViewItem(fileInfo.DirectoryName)
Line 1074... Line 1097...
1074 toolStripProgressBar1.Style = ProgressBarStyle.Continuous; 1097 toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
1075 toolStripProgressBar1.Minimum = 0; 1098 toolStripProgressBar1.Minimum = 0;
1076 toolStripProgressBar1.Maximum = 1; 1099 toolStripProgressBar1.Maximum = 1;
1077 toolStripProgressBar1.Value = 0; 1100 toolStripProgressBar1.Value = 0;
Line 1078... Line -...
1078   -  
1079 void QuickImageListViewItemProgress(object sender, 1101  
1080 ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e) 1102 void QuickImageListViewItemProgress(object sender, ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e)
1081 { 1103 {
1082 switch (e) 1104 switch (e)
1083 { 1105 {
1084 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)> 1106 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)>
1085 imageListViewItemProgressSuccess: 1107 imageListViewItemProgressSuccess:
1086 if (imageListViewItemProgressSuccess.Item is { } tuple) 1108 if (imageListViewItemProgressSuccess.Item is { } tuple)
1087 { 1109 {
Line 1088... Line 1110...
1088 var (item, image) = tuple; 1110 var (item, image) = tuple;
-   1111  
1089   1112 imageListView.BeginUpdate();
1090 imageListView.BeginUpdate(); 1113  
1091 try 1114 try
Line 1092... Line 1115...
1092 { 1115 {
Line 1093... Line 1116...
1093 imageListView.Items.Remove(item); 1116 imageListView.Items.Remove(item);
1094   1117  
1095 var fileInfo = new FileInfo(image.File); 1118 var fileInfo = new FileInfo(image.File);
1096   -  
1097 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group)) 1119  
1098 { 1120 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group))
1099 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) 1121 {
Line 1100... Line 1122...
1100 { Name = fileInfo.DirectoryName }; 1122 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) { Name = fileInfo.DirectoryName };
Line 1147... Line 1169...
1147 _quickImageListViewProgress.ProgressChanged += QuickImageListViewItemProgress; 1169 _quickImageListViewProgress.ProgressChanged += QuickImageListViewItemProgress;
1148 try 1170 try
1149 { 1171 {
1150 var directoryName = Path.GetDirectoryName(item.Name); 1172 var directoryName = Path.GetDirectoryName(item.Name);
Line 1151... Line 1173...
1151   1173  
1152 await RenameImage(item, Path.Combine(directoryName, destinationFileName), _quickImageListViewProgress, -  
1153 cancellationToken); 1174 await RenameImage(item, Path.Combine(directoryName, destinationFileName), _quickImageListViewProgress, cancellationToken);
1154 } 1175 }
1155 catch (Exception exception) 1176 catch (Exception exception)
1156 { 1177 {
Line 1169... Line 1190...
1169 toolStripProgressBar1.Style = ProgressBarStyle.Continuous; 1190 toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
1170 toolStripProgressBar1.Minimum = 0; 1191 toolStripProgressBar1.Minimum = 0;
1171 toolStripProgressBar1.Maximum = enumerable.Length; 1192 toolStripProgressBar1.Maximum = enumerable.Length;
1172 toolStripProgressBar1.Value = 0; 1193 toolStripProgressBar1.Value = 0;
Line 1173... Line -...
1173   -  
1174 void QuickImageListViewItemProgress(object sender, 1194  
1175 ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e) 1195 void QuickImageListViewItemProgress(object sender, ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)> e)
1176 { 1196 {
1177 switch (e) 1197 switch (e)
1178 { 1198 {
1179 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)> -  
1180 imageListViewItemProgressSuccess: 1199 case ImageListViewItemProgressSuccess<(ListViewItem Item, Database.QuickImage Image)> imageListViewItemProgressSuccess:
1181 if (imageListViewItemProgressSuccess.Item is { } tuple) 1200 if (imageListViewItemProgressSuccess.Item is { } tuple)
1182 { 1201 {
Line 1183... Line 1202...
1183 var (item, image) = tuple; 1202 var (item, image) = tuple;
Line 1205... Line 1224...
1205 Name = image.File, 1224 Name = image.File,
1206 ImageKey = image.File, 1225 ImageKey = image.File,
1207 Text = fileInfo.Name, 1226 Text = fileInfo.Name,
1208 Group = group 1227 Group = group
1209 }); 1228 });
-   1229  
1210 imageListView.EnsureVisible(listViewItem.Index); 1230 imageListView.EnsureVisible(listViewItem.Index);
1211 } 1231 }
1212 finally 1232 finally
1213 { 1233 {
1214 imageListView.EndUpdate(); 1234 imageListView.EndUpdate();
Line 1285... Line 1305...
1285 return; 1305 return;
1286 } 1306 }
Line 1287... Line 1307...
1287   1307  
1288 try 1308 try
1289 { -  
1290 using var _ = bufferBlock.LinkTo(actionBlock, 1309 {
Line 1291... Line 1310...
1291 new DataflowLinkOptions { PropagateCompletion = true }); 1310 using var _ = bufferBlock.LinkTo(actionBlock, new DataflowLinkOptions { PropagateCompletion = true });
1292   1311  
1293 foreach (var item in enumerable) 1312 foreach (var item in enumerable)
-   1313 {
-   1314 if (cancellationToken.IsCancellationRequested)
-   1315 {
Line 1294... Line 1316...
1294 { 1316 return;
1295 if (cancellationToken.IsCancellationRequested) return; 1317 }
1296   1318  
Line 1297... Line 1319...
1297 try 1319 try
-   1320 {
-   1321 File.Delete(item.Name);
-   1322  
Line 1298... Line 1323...
1298 { 1323 if (!await _quickImageDatabase.RemoveImageAsync(item.Name, cancellationToken))
1299 File.Delete(item.Name); 1324 {
1300   1325 continue;
1301 if (!await _quickImageDatabase.RemoveImageAsync(item.Name, cancellationToken)) continue; 1326 }
Line 1329... Line 1354...
1329 catch 1354 catch
1330 { 1355 {
1331 return; 1356 return;
1332 } 1357 }
Line -... Line 1358...
-   1358  
-   1359 if (_selectionCancellationTokenSource != null)
1333   1360 {
-   1361 _selectionCancellationTokenSource.Cancel();
Line 1334... Line 1362...
1334 if (_selectionCancellationTokenSource != null) _selectionCancellationTokenSource.Cancel(); 1362 }
-   1363  
1335   1364 imageListView.InvokeIfRequired(view => { imageListView.BeginUpdate(); });
1336 imageListView.InvokeIfRequired(view => { imageListView.BeginUpdate(); }); 1365  
1337 try 1366 try
1338 { 1367 {
1339 var taskCompletionSource = new TaskCompletionSource<object>(); 1368 var taskCompletionSource = new TaskCompletionSource<object>();
1340 imageListView.InvokeIfRequired(view => 1369 imageListView.InvokeIfRequired(view =>
-   1370 {
-   1371 foreach (var item in view.Items.OfType<ListViewItem>())
-   1372 {
Line 1341... Line 1373...
1341 { 1373 _searchStore.TryAdd(item.Name, item);
-   1374 }
1342 foreach (var item in view.Items.OfType<ListViewItem>()) _searchStore.TryAdd(item.Name, item); 1375  
1343   1376 view.Items.Clear();
Line 1344... Line 1377...
1344 view.Items.Clear(); 1377  
Line 1345... Line -...
1345 taskCompletionSource.TrySetResult(new { }); -  
1346 }); 1378 taskCompletionSource.TrySetResult(new { });
1347   -  
1348 await taskCompletionSource.Task; -  
1349   1379 });
1350 await foreach (var quickImage in _quickImageDatabase 1380  
-   1381 await taskCompletionSource.Task;
-   1382  
-   1383 await foreach (var quickImage in _quickImageDatabase.Search(keywords, _quickImageSearchType, _quickImageSearchParameters, _combinedSearchSelectionCancellationToken).WithCancellation(_combinedSearchSelectionCancellationToken))
Line 1351... Line 1384...
1351 .Search(keywords, _quickImageSearchType, _quickImageSearchParameters, 1384 {
1352 _combinedSearchSelectionCancellationToken) 1385 if (!_searchStore.TryGetValue(quickImage.File, out var item))
-   1386 {
-   1387 continue;
-   1388 }
Line 1353... Line 1389...
1353 .WithCancellation(_combinedSearchSelectionCancellationToken)) 1389  
1354 { 1390 var directoryName = Path.GetDirectoryName(item.Name);
1355 if (!_searchStore.TryGetValue(quickImage.File, out var item)) continue; 1391 if (_imageListViewGroupDictionary.TryGetValue(directoryName, out var group))
1356   1392 {
Line 1380... Line 1416...
1380 catch 1416 catch
1381 { 1417 {
1382 return; 1418 return;
1383 } 1419 }
Line -... Line 1420...
-   1420  
-   1421 if (_selectionCancellationTokenSource != null)
1384   1422 {
-   1423 _selectionCancellationTokenSource.Cancel();
Line 1385... Line 1424...
1385 if (_selectionCancellationTokenSource != null) _selectionCancellationTokenSource.Cancel(); 1424 }
1386   1425  
1387 imageListView.InvokeIfRequired(view => { imageListView.BeginUpdate(); }); 1426 imageListView.InvokeIfRequired(view => { imageListView.BeginUpdate(); });
1388 try 1427 try
Line 1389... Line 1428...
1389 { 1428 {
-   1429 toolStripStatusLabel1.Text = "Restoring items.";
1390 toolStripStatusLabel1.Text = "Restoring items."; 1430  
1391   1431 var taskCompletionSource = new TaskCompletionSource<object>();
1392 var taskCompletionSource = new TaskCompletionSource<object>(); 1432  
1393 imageListView.InvokeIfRequired(view => 1433 imageListView.InvokeIfRequired(view =>
1394 { 1434 {
Line 1429... Line 1469...
1429   1469  
1430 private async Task OcrImages(IEnumerable<ListViewItem> items, 1470 private async Task OcrImages(IEnumerable<ListViewItem> items,
1431 IProgress<ImageListViewItemProgress<ListViewItem>> progress, 1471 IProgress<ImageListViewItemProgress<ListViewItem>> progress,
1432 CancellationToken cancellationToken) 1472 CancellationToken cancellationToken)
1433 { 1473 {
1434 using var engine = new TesseractEngine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tessdata"), -  
1435 "eng", EngineMode.Default); 1474 using var engine = new TesseractEngine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tessdata"), "eng", EngineMode.Default);
1436 foreach (var item in items) 1475 foreach (var item in items)
1437 { 1476 {
Line 1438... Line 1477...
1438 if (cancellationToken.IsCancellationRequested) return; 1477 if (cancellationToken.IsCancellationRequested) return;
Line 1462... Line 1501...
1462 continue; 1501 continue;
1463 } 1502 }
Line 1464... Line 1503...
1464   1503  
1465 if (!await _quickImageDatabase.AddTagsAsync(item.Name, tags, cancellationToken)) 1504 if (!await _quickImageDatabase.AddTagsAsync(item.Name, tags, cancellationToken))
1466 { 1505 {
1467 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item, -  
1468 new ArgumentException(MethodBase.GetCurrentMethod()?.Name))); 1506 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item, new ArgumentException(MethodBase.GetCurrentMethod()?.Name)));
1469 continue; 1507 continue;
Line 1470... Line 1508...
1470 } 1508 }
1471   1509  
Line 1482... Line 1520...
1482 IProgress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>> progress, 1520 IProgress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>> progress,
1483 CancellationToken cancellationToken) 1521 CancellationToken cancellationToken)
1484 { 1522 {
1485 foreach (var item in items) 1523 foreach (var item in items)
1486 { 1524 {
1487 if (cancellationToken.IsCancellationRequested) return; 1525 if (cancellationToken.IsCancellationRequested)
-   1526 {
-   1527 return;
-   1528 }
Line 1488... Line 1529...
1488   1529  
1489 try 1530 try
1490 { 1531 {
Line 1564... Line 1605...
1564 CancellationToken cancellationToken) 1605 CancellationToken cancellationToken)
1565 { 1606 {
1566 try 1607 try
1567 { 1608 {
1568 await Miscellaneous.CopyFileAsync(item.Name, destinationFileName, cancellationToken); 1609 await Miscellaneous.CopyFileAsync(item.Name, destinationFileName, cancellationToken);
-   1610  
1569 File.Delete(item.Name); 1611 File.Delete(item.Name);
Line 1570... Line 1612...
1570   1612  
Line 1571... Line 1613...
1571 var image = await _quickImageDatabase.GetImageAsync(item.Name, cancellationToken); 1613 var image = await _quickImageDatabase.GetImageAsync(item.Name, cancellationToken);
Line 1606... Line 1648...
1606 IProgress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>> progress, 1648 IProgress<ImageListViewItemProgress<(ListViewItem Item, Database.QuickImage Image)>> progress,
1607 CancellationToken cancellationToken) 1649 CancellationToken cancellationToken)
1608 { 1650 {
1609 foreach (var item in items) 1651 foreach (var item in items)
1610 { 1652 {
1611 if (cancellationToken.IsCancellationRequested) return; 1653 if (cancellationToken.IsCancellationRequested)
-   1654 {
-   1655 return;
-   1656 }
Line 1612... Line 1657...
1612   1657  
1613 try 1658 try
1614 { 1659 {
1615 var fileName = Path.GetFileName(item.Name); 1660 var fileName = Path.GetFileName(item.Name);
Line 1655... Line 1700...
1655 private async Task GetTags(IReadOnlyList<ListViewItem> items, 1700 private async Task GetTags(IReadOnlyList<ListViewItem> items,
1656 IProgress<ImageListViewItemProgress<ListViewItem>> progress, CancellationToken cancellationToken) 1701 IProgress<ImageListViewItemProgress<ListViewItem>> progress, CancellationToken cancellationToken)
1657 { 1702 {
1658 foreach (var item in items) 1703 foreach (var item in items)
1659 { 1704 {
1660 if (cancellationToken.IsCancellationRequested) return; 1705 if (cancellationToken.IsCancellationRequested)
-   1706 {
-   1707 return;
-   1708 }
Line 1661... Line 1709...
1661   1709  
1662 try 1710 try
1663 { 1711 {
1664 var tags = await _quickImageDatabase.GetTags(item.Name, cancellationToken) 1712 var tags = await _quickImageDatabase.GetTags(item.Name, cancellationToken)
Line 1770... Line 1818...
1770   1818  
1771 try 1819 try
1772 { 1820 {
Line 1773... Line 1821...
1773 var mime = await magicMime.GetMimeType(item.Name, cancellationToken); 1821 var mime = await magicMime.GetMimeType(item.Name, cancellationToken);
-   1822  
1774   1823 if (Configuration.SupportedFormats.IsSupportedImage(mime))
1775 if (Configuration.SupportedFormats.IsSupportedImage(mime)) 1824 {
1776 if (!await _tagManager.StripIptcProfile(item.Name, cancellationToken)) 1825 if (!await _tagManager.StripIptcProfile(item.Name, cancellationToken))
1777 { 1826 {
1778 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item, 1827 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item,
1779 new ArgumentException(MethodBase.GetCurrentMethod()?.Name))); 1828 new ArgumentException(MethodBase.GetCurrentMethod()?.Name)));
-   1829 continue;
Line 1780... Line 1830...
1780 continue; 1830 }
1781 } -  
Line 1782... Line 1831...
1782   1831 }
1783 var tags = await _quickImageDatabase.GetTags(item.Name, cancellationToken) 1832  
1784 .ToArrayAsync(cancellationToken); 1833 var tags = await _quickImageDatabase.GetTags(item.Name, cancellationToken).ToArrayAsync(cancellationToken);
1785   1834  
Line 1813... Line 1862...
1813   1862  
1814 try 1863 try
1815 { 1864 {
Line 1816... Line 1865...
1816 var mime = await magicMime.GetMimeType(item.Name, cancellationToken); 1865 var mime = await magicMime.GetMimeType(item.Name, cancellationToken);
-   1866  
1817   1867 if (Configuration.SupportedFormats.IsSupportedImage(mime))
1818 if (Configuration.SupportedFormats.IsSupportedImage(mime)) 1868 {
1819 if (!await _tagManager.RemoveIptcKeywords(item.Name, keywords, cancellationToken)) 1869 if (!await _tagManager.RemoveIptcKeywords(item.Name, keywords, cancellationToken))
1820 { 1870 {
1821 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item, 1871 progress.Report(new ImageListViewItemProgressFailure<ListViewItem>(item,
1822 new ArgumentException(MethodBase.GetCurrentMethod()?.Name))); 1872 new ArgumentException(MethodBase.GetCurrentMethod()?.Name)));
-   1873 continue;
Line 1823... Line 1874...
1823 continue; 1874 }
1824 } 1875 }
1825   1876  
1826 if (!await _quickImageDatabase.RemoveTagsAsync(item.Name, keywords, cancellationToken)) 1877 if (!await _quickImageDatabase.RemoveTagsAsync(item.Name, keywords, cancellationToken))
Line 1901... Line 1952...
1901 DateImageListViewSorterType.Modification)); 1952 DateImageListViewSorterType.Modification));
1902 } 1953 }
Line 1903... Line 1954...
1903   1954  
1904 private void renameToolStripMenuItem_Click(object sender, EventArgs e) 1955 private void renameToolStripMenuItem_Click(object sender, EventArgs e)
1905 { 1956 {
-   1957 if (_renameForm != null)
-   1958 {
-   1959 return;
Line 1906... Line 1960...
1906 if (_renameForm != null) return; 1960 }
Line 1907... Line 1961...
1907   1961  
-   1962 var item = imageListView.SelectedItems.OfType<ListViewItem>().FirstOrDefault();
-   1963  
-   1964 if (item == null)
Line 1908... Line 1965...
1908 var item = imageListView.SelectedItems.OfType<ListViewItem>().FirstOrDefault(); 1965 {
1909   1966 return;
1910 if (item == null) return; 1967 }
1911   1968  
Line 1920... Line 1977...
1920 await RenameImageAsync(e.ListViewItem, e.FileName, _cancellationToken); 1977 await RenameImageAsync(e.ListViewItem, e.FileName, _cancellationToken);
1921 } 1978 }
Line 1922... Line 1979...
1922   1979  
1923 private void RenameForm_Closing(object sender, CancelEventArgs e) 1980 private void RenameForm_Closing(object sender, CancelEventArgs e)
1924 { 1981 {
-   1982 if (_renameForm == null)
-   1983 {
-   1984 return;
Line 1925... Line 1985...
1925 if (_renameForm == null) return; 1985 }
1926   1986  
1927 _renameForm.Closing -= RenameForm_Closing; 1987 _renameForm.Closing -= RenameForm_Closing;
1928 _renameForm.Dispose(); 1988 _renameForm.Dispose();
Line 1938... Line 1998...
1938 IsFolderPicker = true 1998 IsFolderPicker = true
1939 }; 1999 };
Line 1940... Line 2000...
1940   2000  
Line 1941... Line 2001...
1941 var groupItems = new List<ListViewItem>(); 2001 var groupItems = new List<ListViewItem>();
-   2002  
1942   2003 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
-   2004 {
1943 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 2005 foreach (var item in imageListView.SelectedItems.OfType<ListViewItem>())
-   2006 {
1944 foreach (var item in imageListView.SelectedItems.OfType<ListViewItem>()) 2007 foreach (var groupItem in item.Group.Items.OfType<ListViewItem>())
-   2008 {
-   2009 groupItems.Add(groupItem);
-   2010 }
Line 1945... Line 2011...
1945 foreach (var groupItem in item.Group.Items.OfType<ListViewItem>()) 2011 }
1946 groupItems.Add(groupItem); 2012 }
Line 1947... Line 2013...
1947   2013  
1948 await RelocateToAsync(groupItems, dialog.FileName, _cancellationToken); 2014 RelocateTo(groupItems, dialog.FileName, _cancellationToken);
1949 } 2015 }
Line 1983... Line 2049...
1983 _searchCancellationToken = _searchCancellationTokenSource.Token; 2049 _searchCancellationToken = _searchCancellationTokenSource.Token;
1984 _linkedSearchCancellationTokenSource = 2050 _linkedSearchCancellationTokenSource =
1985 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken); 2051 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken);
1986 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token; 2052 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token;
Line 1987... Line 2053...
1987   2053  
1988 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, -  
1989 async text => { await BeginSearch(text); }, _formTaskScheduler, -  
1990 _combinedSearchSelectionCancellationToken); 2054 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, async text => { await BeginSearch(text); }, _formTaskScheduler, _combinedSearchSelectionCancellationToken);
Line 1991... Line 2055...
1991 } 2055 }
1992   2056  
1993 private void checkBox3_CheckedChanged(object sender, EventArgs e) 2057 private void checkBox3_CheckedChanged(object sender, EventArgs e)
Line 2005... Line 2069...
2005 _quickImageSearchParameters & ~QuickImageSearchParameters.Split; 2069 _quickImageSearchParameters & ~QuickImageSearchParameters.Split;
2006 break; 2070 break;
2007 } 2071 }
Line 2008... Line 2072...
2008   2072  
2009 var text = textBox1.Text; 2073 var text = textBox1.Text;
-   2074 if (string.IsNullOrEmpty(text))
-   2075 {
-   2076 return;
Line -... Line 2077...
-   2077 }
-   2078  
2010 if (string.IsNullOrEmpty(text)) return; 2079 if (_searchCancellationTokenSource != null)
-   2080 {
Line 2011... Line 2081...
2011   2081 _searchCancellationTokenSource.Cancel();
2012 if (_searchCancellationTokenSource != null) _searchCancellationTokenSource.Cancel(); 2082 }
2013   2083  
2014 _searchCancellationTokenSource = new CancellationTokenSource(); 2084 _searchCancellationTokenSource = new CancellationTokenSource();
2015 _searchCancellationToken = _searchCancellationTokenSource.Token; 2085 _searchCancellationToken = _searchCancellationTokenSource.Token;
Line 2016... Line 2086...
2016 _linkedSearchCancellationTokenSource = 2086 _linkedSearchCancellationTokenSource =
2017 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken); -  
2018 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token; -  
2019   2087 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken);
Line 2020... Line 2088...
2020 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, 2088 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token;
2021 async text => { await BeginSearch(text); }, _formTaskScheduler, 2089  
2022 _combinedSearchSelectionCancellationToken); 2090 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, async text => { await BeginSearch(text); }, _formTaskScheduler, _combinedSearchSelectionCancellationToken);
Line 2059... Line 2127...
2059   2127  
2060 private async Task PerformUpgrade() 2128 private async Task PerformUpgrade()
2061 { 2129 {
2062 // Manually check for updates, this will not show a ui 2130 // Manually check for updates, this will not show a ui
-   2131 var updateCheck = await _sparkle.CheckForUpdatesQuietly();
2063 var updateCheck = await _sparkle.CheckForUpdatesQuietly(); 2132  
2064 switch (updateCheck.Status) 2133 switch (updateCheck.Status)
2065 { 2134 {
2066 case UpdateStatus.UserSkipped: 2135 case UpdateStatus.UserSkipped:
-   2136 var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version;
2067 var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version; 2137  
2068 updateCheck.Updates.Sort(UpdateComparer); 2138 updateCheck.Updates.Sort(UpdateComparer);
2069 var latestVersion = updateCheck.Updates.FirstOrDefault(); 2139 var latestVersion = updateCheck.Updates.FirstOrDefault();
2070 if (latestVersion != null) 2140 if (latestVersion != null)
2071 { 2141 {
Line 2143... Line 2213...
2143 if (fileAttributes.HasFlag(FileAttributes.Directory)) 2213 if (fileAttributes.HasFlag(FileAttributes.Directory))
2144 { 2214 {
2145 continue; 2215 continue;
2146 } 2216 }
Line -... Line 2217...
-   2217  
2147   2218 using var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
-   2219 var memoryStream = new MemoryStream();
2148 var memoryStream = new MemoryStream(File.ReadAllBytes(file)); 2220 await fileStream.CopyToAsync(memoryStream);
Line 2149... Line 2221...
2149 memoryStream.Position = 0L; 2221 memoryStream.Position = 0L;
2150   2222  
2151 var mime = await magicMime.Identify(file, cancellationToken); 2223 var mime = await magicMime.Identify(file, cancellationToken);
Line 2163... Line 2235...
2163 var fileNames = data.GetFileContentNames(); 2235 var fileNames = data.GetFileContentNames();
2164 for (var i = 0; i < fileNames.Length; ++i) 2236 for (var i = 0; i < fileNames.Length; ++i)
2165 { 2237 {
2166 var memoryStream = data.GetFileContent(i); 2238 var memoryStream = data.GetFileContent(i);
2167 memoryStream.Position = 0L; 2239 memoryStream.Position = 0L;
-   2240  
2168 var mime = magicMime.Identify(fileNames[i], memoryStream, cancellationToken); 2241 var mime = magicMime.Identify(fileNames[i], memoryStream, cancellationToken);
-   2242  
2169 if (mime == null) continue; 2243 if (mime == null)
-   2244 {
-   2245 continue;
-   2246 }
-   2247  
2170 yield return (File: fileNames[0], Data: memoryStream, Mime: mime.Definition); 2248 yield return (File: fileNames[0], Data: memoryStream, Mime: mime.Definition);
2171 } 2249 }
2172 } 2250 }
Line 2173... Line 2251...
2173   2251  
2174 private static async IAsyncEnumerable<string> GetFilesAsync(string entry, 2252 private static async IAsyncEnumerable<string> GetFilesAsync(string entry,
2175 Configuration.Configuration configuration, MagicMime magicMime, 2253 Configuration.Configuration configuration, MagicMime magicMime,
2176 [EnumeratorCancellation] CancellationToken cancellationToken) 2254 [EnumeratorCancellation] CancellationToken cancellationToken)
2177 { 2255 {
2178 var bufferBlock = new BufferBlock<string>(new DataflowBlockOptions -  
Line 2179... Line 2256...
2179 { CancellationToken = cancellationToken }); 2256 var bufferBlock = new BufferBlock<string>(new DataflowBlockOptions { CancellationToken = cancellationToken });
2180   2257  
2181 #pragma warning disable CS4014 2258 #pragma warning disable CS4014
2182 Task.Run(async () => 2259 Task.Run(async () =>
Line 2222... Line 2299...
2222 } 2299 }
2223 }, cancellationToken); 2300 }, cancellationToken);
Line 2224... Line 2301...
2224   2301  
2225 while (await bufferBlock.OutputAvailableAsync(cancellationToken)) 2302 while (await bufferBlock.OutputAvailableAsync(cancellationToken))
2226 { 2303 {
-   2304 if (!bufferBlock.TryReceiveAll(out var files))
-   2305 {
-   2306 continue;
Line 2227... Line 2307...
2227 if (!bufferBlock.TryReceiveAll(out var files)) continue; 2307 }
-   2308  
-   2309 foreach (var file in files)
-   2310 {
2228   2311 yield return file;
2229 foreach (var file in files) yield return file; 2312 }
Line 2230... Line 2313...
2230 } 2313 }
2231 } 2314 }
Line 2331... Line 2414...
2331   2414  
Line 2332... Line 2415...
2332 #region Event Handlers 2415 #region Event Handlers
2333   2416  
2334 private void toolStripTextBox1_KeyUp(object sender, KeyEventArgs e) 2417 private void toolStripTextBox1_KeyUp(object sender, KeyEventArgs e)
-   2418 {
-   2419 if (e.KeyCode != Keys.Return)
-   2420 {
Line 2335... Line 2421...
2335 { 2421 return;
2336 if (e.KeyCode != Keys.Return) return; 2422 }
Line 2337... Line 2423...
2337   2423  
2338 // Skip the beep. 2424 // Skip the beep.
2339 e.Handled = true; 2425 e.Handled = true;
Line 2340... Line 2426...
2340   2426  
-   2427 var toolStripTextBox = (ToolStripTextBox)sender;
-   2428 var tagText = toolStripTextBox.Text;
-   2429 toolStripTextBox.Clear();
Line 2341... Line 2430...
2341 var toolStripTextBox = (ToolStripTextBox)sender; 2430  
Line 2342... Line 2431...
2342 var tagText = toolStripTextBox.Text; 2431 if (string.IsNullOrEmpty(tagText))
Line 2397... Line 2486...
2397 } 2486 }
Line 2398... Line 2487...
2398   2487  
2399 toolStripStatusLabel1.Text = "Adding tags..."; 2488 toolStripStatusLabel1.Text = "Adding tags...";
Line 2400... Line 2489...
2400 toolStripProgressBar1.Increment(1); 2489 toolStripProgressBar1.Increment(1);
-   2490  
2401   2491 if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum)
-   2492 {
2402 if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum) 2493 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress;
Line 2403... Line 2494...
2403 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress; 2494 }
2404 } 2495 }
2405   2496  
Line 2474... Line 2565...
2474 form.toolStripProgressBar1.MarqueeAnimationSpeed = 30; 2565 form.toolStripProgressBar1.MarqueeAnimationSpeed = 30;
2475 }); 2566 });
Line 2476... Line 2567...
2476   2567  
2477 try 2568 try
2478 { -  
2479 var images = await _quickImageDatabase.GetAll(_cancellationToken).ToArrayAsync(_cancellationToken); 2569 {
2480 var imageListViewItems = new List<ListViewItem>(); 2570 var imageListViewItems = new ConcurrentBag<ListViewItem>();
2481 var tags = new HashSet<string>(StringComparer.Ordinal); 2571 var tags = new HashSet<string>(StringComparer.Ordinal);
2482 foreach (var image in images) 2572 await foreach (var image in _quickImageDatabase.GetAll(_cancellationToken))
2483 { 2573 {
-   2574 if (!largeImageList.Images.ContainsKey(image.File))
2484 if (!largeImageList.Images.ContainsKey(image.File)) 2575 {
-   2576 largeImageList.Images.Add(image.File, image.Thumbnail);
Line 2485... Line 2577...
2485 largeImageList.Images.Add(image.File, image.Thumbnail); 2577 }
Line 2486... Line 2578...
2486   2578  
2487 var fileInfo = new FileInfo(image.File); 2579 var fileInfo = new FileInfo(image.File);
2488   2580  
2489 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group)) -  
2490 { -  
Line -... Line 2581...
-   2581 if (!_imageListViewGroupDictionary.TryGetValue(fileInfo.DirectoryName, out var group))
-   2582 {
2491 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) 2583 group = new ListViewGroup(fileInfo.DirectoryName, HorizontalAlignment.Left) { Name = fileInfo.DirectoryName };
2492 { Name = fileInfo.DirectoryName }; 2584  
-   2585 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group);
2493 _imageListViewGroupDictionary.TryAdd(fileInfo.DirectoryName, group); 2586  
2494   -  
-   2587 imageListView.InvokeIfRequired(view =>
2495 imageListView.InvokeIfRequired(view => 2588 {
2496 { 2589 view.BeginUpdate();
Line 2497... Line 2590...
2497 view.Groups.Add(group); 2590 view.Groups.Add(group);
Line 2498... Line 2591...
2498   2591 view.EndUpdate();
2499 }); 2592 });
2500 } 2593 }
2501   2594  
2502 tags.UnionWith(image.Tags); 2595 tags.UnionWith(image.Tags);
2503   2596  
2504 imageListViewItems.Add(new ListViewItem(image.File) 2597 var imageListViewItem = new ListViewItem(image.File)
-   2598 {
-   2599 Name = image.File,
-   2600 ImageKey = image.File,
2505 { 2601 Text = fileInfo.Name,
Line 2506... Line 2602...
2506 Name = image.File, 2602 Group = group
Line 2507... Line 2603...
2507 ImageKey = image.File, 2603 };
2508 Text = fileInfo.Name, 2604  
Line 2522... Line 2618...
2522 tagListView.InvokeIfRequired(view => 2618 tagListView.InvokeIfRequired(view =>
2523 { 2619 {
2524 view.BeginUpdate(); 2620 view.BeginUpdate();
2525 view.Items.AddRange(tags.Select(tag => new ListViewItem(tag) { Name = tag }).ToArray()); 2621 view.Items.AddRange(tags.Select(tag => new ListViewItem(tag) { Name = tag }).ToArray());
2526 view.EndUpdate(); 2622 view.EndUpdate();
2527 }); 2623 });
2528 } 2624 }
2529 catch (Exception exception) 2625 catch (Exception exception)
2530 { 2626 {
2531 Log.Error(exception, "Unable to load images."); 2627 Log.Error(exception, "Unable to load images.");
2532 } 2628 }
Line 2544... Line 2640...
2544 } 2640 }
Line 2545... Line 2641...
2545   2641  
2546 private async void tagListView_MouseDown(object sender, MouseEventArgs e) 2642 private async void tagListView_MouseDown(object sender, MouseEventArgs e)
2547 { 2643 {
2548 var listView = (ListView)sender; 2644 var listView = (ListView)sender;
-   2645 if (!listView.CheckBoxes)
-   2646 {
-   2647 return;
Line 2549... Line 2648...
2549 if (!listView.CheckBoxes) return; 2648 }
2550   2649  
2551 // Allow clicking anywhere on tag. 2650 // Allow clicking anywhere on tag.
-   2651 var hitTest = listView.HitTest(e.Location);
-   2652 if (hitTest.Item == null)
-   2653 {
Line 2552... Line 2654...
2552 var hitTest = listView.HitTest(e.Location); 2654 return;
Line 2553... Line 2655...
2553 if (hitTest.Item == null) return; 2655 }
Line 2588... Line 2690...
2588 case ImageListViewItemProgressFailure<ListViewItem> imageListViewItemProgressFailure: 2690 case ImageListViewItemProgressFailure<ListViewItem> imageListViewItemProgressFailure:
2589 break; 2691 break;
2590 } 2692 }
Line 2591... Line 2693...
2591   2693  
2592 toolStripProgressBar1.Increment(1); 2694 toolStripProgressBar1.Increment(1);
-   2695 if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum)
2593 if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum) 2696 {
-   2697 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress;
2594 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress; 2698 }
Line 2595... Line 2699...
2595 } 2699 }
2596   2700  
Line 2597... Line 2701...
2597 if (item.Checked) 2701 if (item.Checked)
-   2702 {
2598 { 2703
2599 2704 toolStripStatusLabel1.Text = "Removing tags...";
2600 toolStripStatusLabel1.Text = "Removing tags..."; 2705  
2601 _listViewItemProgress.ProgressChanged += ImageListViewItemProgress; 2706 _listViewItemProgress.ProgressChanged += ImageListViewItemProgress;
2602 try 2707 try
2603 { 2708 {
2604 await RemoveTags(items, keywords, _magicMime, _listViewItemProgress, _cancellationToken); 2709 await RemoveTags(items, keywords, _magicMime, _listViewItemProgress, _cancellationToken);
2605 } 2710 }
2606 finally 2711 finally
Line -... Line 2712...
-   2712 {
-   2713 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress;
-   2714 }
2607 { 2715  
-   2716 switch(hitTest.Location)
-   2717 {
Line 2608... Line 2718...
2608 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress; 2718 case ListViewHitTestLocations.Label:
2609 } 2719 hitTest.Item.Checked = !hitTest.Item.Checked;
Line 2610... Line 2720...
2610   2720 break;
Line 2622... Line 2732...
2622 finally 2732 finally
2623 { 2733 {
2624 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress; 2734 _listViewItemProgress.ProgressChanged -= ImageListViewItemProgress;
2625 } 2735 }
Line -... Line 2736...
-   2736  
-   2737 switch(hitTest.Location)
-   2738 {
2626   2739 case ListViewHitTestLocations.Label:
-   2740 hitTest.Item.Checked = !hitTest.Item.Checked;
-   2741 break;
2627 if (hitTest.Location == ListViewHitTestLocations.Label) hitTest.Item.Checked = !hitTest.Item.Checked; 2742 }
Line 2628... Line 2743...
2628 } 2743 }
2629   2744  
2630 private void aboutToolStripMenuItem_Click(object sender, EventArgs e) 2745 private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
-   2746 {
-   2747 if (_aboutForm != null)
-   2748 {
Line 2631... Line 2749...
2631 { 2749 return;
2632 if (_aboutForm != null) return; 2750 }
2633   2751  
2634 _aboutForm = new AboutForm(); 2752 _aboutForm = new AboutForm();
Line 2635... Line 2753...
2635 _aboutForm.Closing += AboutForm_Closing; 2753 _aboutForm.Closing += AboutForm_Closing;
2636 _aboutForm.Show(); 2754 _aboutForm.Show();
2637 } 2755 }
-   2756  
-   2757 private void AboutForm_Closing(object sender, CancelEventArgs e)
-   2758 {
Line 2638... Line 2759...
2638   2759 if (_aboutForm == null)
2639 private void AboutForm_Closing(object sender, CancelEventArgs e) 2760 {
2640 { 2761 return;
2641 if (_aboutForm == null) return; 2762 }
Line 2650... Line 2771...
2650 await PerformUpgrade(); 2771 await PerformUpgrade();
2651 } 2772 }
Line 2652... Line 2773...
2652   2773  
2653 private void viewLogsToolStripMenuItem_Click(object sender, EventArgs e) 2774 private void viewLogsToolStripMenuItem_Click(object sender, EventArgs e)
2654 { 2775 {
-   2776 if (_viewLogsForm != null)
-   2777 {
-   2778 return;
Line 2655... Line 2779...
2655 if (_viewLogsForm != null) return; 2779 }
2656   2780  
2657 _viewLogsForm = new ViewLogsForm(this, _memorySink, _cancellationToken); 2781 _viewLogsForm = new ViewLogsForm(this, _memorySink, _cancellationToken);
2658 _viewLogsForm.Closing += ViewLogsForm_Closing; 2782 _viewLogsForm.Closing += ViewLogsForm_Closing;
Line 2659... Line 2783...
2659 _viewLogsForm.Show(); 2783 _viewLogsForm.Show();
2660 } 2784 }
2661   2785  
-   2786 private void ViewLogsForm_Closing(object sender, CancelEventArgs e)
-   2787 {
-   2788 if (_viewLogsForm == null)
Line 2662... Line 2789...
2662 private void ViewLogsForm_Closing(object sender, CancelEventArgs e) 2789 {
2663 { 2790 return;
2664 if (_viewLogsForm == null) return; 2791 }
2665   2792  
Line 2676... Line 2803...
2676   2803  
2677 private async void removeToolStripMenuItem_Click(object sender, EventArgs e) 2804 private async void removeToolStripMenuItem_Click(object sender, EventArgs e)
2678 { 2805 {
Line 2679... Line 2806...
2679 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray(); 2806 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray();
2680   2807  
Line 2681... Line 2808...
2681 await RemoveImagesAsync(items, _cancellationToken); 2808 RemoveImages(items, _cancellationToken);
2682 } 2809 }
2683   2810  
Line 2750... Line 2877...
2750 private void textBox1_KeyDown(object sender, KeyEventArgs e) 2877 private void textBox1_KeyDown(object sender, KeyEventArgs e)
2751 { 2878 {
2752 if (e.Control && e.KeyCode == Keys.A) 2879 if (e.Control && e.KeyCode == Keys.A)
2753 { 2880 {
2754 var textBox = (TextBox)sender; 2881 var textBox = (TextBox)sender;
-   2882  
2755 textBox.SelectAll(); 2883 textBox.SelectAll();
2756 } 2884 }
2757 } 2885 }
Line 2758... Line 2886...
2758   2886  
2759 private void radioButton2_CheckedChanged(object sender, EventArgs e) 2887 private void radioButton2_CheckedChanged(object sender, EventArgs e)
2760 { 2888 {
Line 2761... Line 2889...
2761 _quickImageSearchType = QuickImageSearchType.Any; 2889 _quickImageSearchType = QuickImageSearchType.Any;
2762   2890  
-   2891 var text = textBox1.Text;
-   2892 if (string.IsNullOrEmpty(text))
-   2893 {
Line -... Line 2894...
-   2894 return;
-   2895 }
2763 var text = textBox1.Text; 2896  
-   2897 if (_searchCancellationTokenSource != null)
Line 2764... Line 2898...
2764 if (string.IsNullOrEmpty(text)) return; 2898 {
2765   2899 _searchCancellationTokenSource.Cancel();
2766 if (_searchCancellationTokenSource != null) _searchCancellationTokenSource.Cancel(); 2900 }
2767   2901  
2768 _searchCancellationTokenSource = new CancellationTokenSource(); 2902 _searchCancellationTokenSource = new CancellationTokenSource();
Line 2769... Line 2903...
2769 _searchCancellationToken = _searchCancellationTokenSource.Token; 2903 _searchCancellationToken = _searchCancellationTokenSource.Token;
2770 _linkedSearchCancellationTokenSource = -  
2771 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken); -  
2772 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token; 2904 _linkedSearchCancellationTokenSource =
Line 2773... Line 2905...
2773   2905 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken);
2774 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, 2906 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token;
2775 async text => { await BeginSearch(text); }, _formTaskScheduler, 2907  
Line 2776... Line 2908...
2776 _combinedSearchSelectionCancellationToken); 2908 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, async text => { await BeginSearch(text); }, _formTaskScheduler, _combinedSearchSelectionCancellationToken);
2777 } 2909 }
-   2910  
-   2911 private void radiobutton1_CheckedChanged(object sender, EventArgs e)
-   2912 {
Line 2778... Line 2913...
2778   2913 _quickImageSearchType = QuickImageSearchType.All;
Line 2779... Line 2914...
2779 private void radiobutton1_CheckedChanged(object sender, EventArgs e) 2914  
2780 { 2915 var text = textBox1.Text;
2781 _quickImageSearchType = QuickImageSearchType.All; 2916 if (string.IsNullOrEmpty(text))
2782   2917 {
2783 var text = textBox1.Text; 2918 return;
Line 2784... Line 2919...
2784 if (string.IsNullOrEmpty(text)) return; 2919 }
2785   -  
2786 if (_searchCancellationTokenSource != null) _searchCancellationTokenSource.Cancel(); -  
2787   2920  
Line 2788... Line 2921...
2788 _searchCancellationTokenSource = new CancellationTokenSource(); 2921 if (_searchCancellationTokenSource != null) _searchCancellationTokenSource.Cancel();
2789 _searchCancellationToken = _searchCancellationTokenSource.Token; 2922  
2790 _linkedSearchCancellationTokenSource = 2923 _searchCancellationTokenSource = new CancellationTokenSource();
2791 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken); 2924 _searchCancellationToken = _searchCancellationTokenSource.Token;
2792 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token; -  
2793   -  
2794 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, -  
2795 async text => { await BeginSearch(text); }, _formTaskScheduler, -  
2796 _combinedSearchSelectionCancellationToken); -  
2797 } 2925 _linkedSearchCancellationTokenSource =
2798   2926 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken);
2799 private async void editToolStripMenuItem1_Click(object sender, EventArgs e) 2927 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token;
2800 { -  
2801 var item = imageListView.SelectedItems.OfType<ListViewItem>().FirstOrDefault(); -  
2802 if (item == null) return; -  
Line -... Line 2928...
-   2928  
-   2929 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, async text => { await BeginSearch(text); }, _formTaskScheduler, _combinedSearchSelectionCancellationToken);
2803   2930 }
2804 if (_editorForm != null) return; 2931  
Line -... Line 2932...
-   2932 private async void editToolStripMenuItem1_Click(object sender, EventArgs e)
2805   2933 {
2806 string mime; 2934 var item = imageListView.SelectedItems.OfType<ListViewItem>().FirstOrDefault();
2807 try 2935 if (item == null)
2808 { 2936 {
2809 mime = await _magicMime.GetMimeType(item.Name, _cancellationToken); 2937 return;
2810 } 2938 }
2811 catch (Exception exception) 2939  
2812 { 2940 if (_editorForm != null)
2813 Log.Error(exception, "Unable to identify file."); 2941 {
2814   2942 return;
Line 2866... Line 2994...
2866 var thumbnail = new Bitmap(thumbnailBitmap); 2994 var thumbnail = new Bitmap(thumbnailBitmap);
2867 thumbnailBitmap.Dispose(); 2995 thumbnailBitmap.Dispose();
Line 2868... Line 2996...
2868   2996  
2869 try 2997 try
2870 { 2998 {
-   2999 if (!await _quickImageDatabase.RemoveImageAsync(e.FileName, _cancellationToken))
2871 var keywords = await _quickImageDatabase.GetTags(e.FileName, _cancellationToken) 3000 {
-   3001 throw new ArgumentException($"Could not remove image {e.FileName} from database.");
Line 2872... Line 3002...
2872 .ToArrayAsync(_cancellationToken); 3002 }
2873   -  
Line 2874... Line 3003...
2874 if (!await _quickImageDatabase.RemoveImageAsync(e.FileName, _cancellationToken)) 3003  
-   3004 var keywords = await _quickImageDatabase.GetTags(e.FileName, _cancellationToken).ToArrayAsync(_cancellationToken);
2875 throw new ArgumentException($"Could not remove image {e.FileName} from database."); 3005  
-   3006 if (!await _quickImageDatabase.AddImageAsync(e.FileName, hash, keywords, thumbnail, _cancellationToken))
Line 2876... Line 3007...
2876   3007 {
2877 if (!await _quickImageDatabase.AddImageAsync(e.FileName, hash, keywords, thumbnail, _cancellationToken)) 3008 throw new ArgumentException($"Could not add image {e.FileName} to database.");
2878 throw new ArgumentException($"Could not add image {e.FileName} to database."); 3009 }
2879   3010  
Line 2889... Line 3020...
2889 } 3020 }
2890 } 3021 }
Line 2891... Line 3022...
2891   3022  
2892 private void _editorForm_Closing(object sender, CancelEventArgs e) 3023 private void _editorForm_Closing(object sender, CancelEventArgs e)
2893 { 3024 {
-   3025 if (_editorForm == null)
-   3026 {
-   3027 return;
Line 2894... Line 3028...
2894 if (_editorForm == null) return; 3028 }
2895   3029  
2896 _editorForm.ImageSave -= _editorForm_ImageSave; 3030 _editorForm.ImageSave -= _editorForm_ImageSave;
2897 _editorForm.ImageSaveAs -= _editorForm_ImageSaveAs; 3031 _editorForm.ImageSaveAs -= _editorForm_ImageSaveAs;
Line 2912... Line 3046...
2912 void ImageListViewItemProgress(object sender, ImageListViewItemProgress<ListViewItem> e) 3046 void ImageListViewItemProgress(object sender, ImageListViewItemProgress<ListViewItem> e)
2913 { 3047 {
2914 switch (e) 3048 switch (e)
2915 { 3049 {
2916 case ImageListViewItemProgressSuccess<ListViewItem> imageListViewItemProgressSuccess: 3050 case ImageListViewItemProgressSuccess<ListViewItem> imageListViewItemProgressSuccess:
2917 if (!(imageListViewItemProgressSuccess.Item is { } listViewItem)) break; 3051 if (!(imageListViewItemProgressSuccess.Item is { } listViewItem))
-   3052 {
-   3053 break;
-   3054 }
Line 2918... Line 3055...
2918   3055  
Line 2919... Line 3056...
2919 toolStripStatusLabel1.Text = $"Synchronizing tags for {listViewItem.Name}"; 3056 toolStripStatusLabel1.Text = $"Synchronizing tags for {listViewItem.Name}";
2920   3057  
Line 2981... Line 3118...
2981 _quickImageSearchParameters & ~QuickImageSearchParameters.CaseSensitive; 3118 _quickImageSearchParameters & ~QuickImageSearchParameters.CaseSensitive;
2982 break; 3119 break;
2983 } 3120 }
Line 2984... Line 3121...
2984   3121  
2985 var text = textBox1.Text; 3122 var text = textBox1.Text;
-   3123 if (string.IsNullOrEmpty(text))
-   3124 {
-   3125 return;
Line -... Line 3126...
-   3126 }
-   3127  
2986 if (string.IsNullOrEmpty(text)) return; 3128 if (_searchCancellationTokenSource != null)
-   3129 {
Line 2987... Line 3130...
2987   3130 _searchCancellationTokenSource.Cancel();
2988 if (_searchCancellationTokenSource != null) _searchCancellationTokenSource.Cancel(); 3131 }
2989   3132  
2990 _searchCancellationTokenSource = new CancellationTokenSource(); 3133 _searchCancellationTokenSource = new CancellationTokenSource();
2991 _searchCancellationToken = _searchCancellationTokenSource.Token; 3134 _searchCancellationToken = _searchCancellationTokenSource.Token;
Line 2992... Line 3135...
2992 _linkedSearchCancellationTokenSource = 3135 _linkedSearchCancellationTokenSource =
2993 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken); -  
2994 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token; -  
2995   3136 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _searchCancellationToken);
Line 2996... Line 3137...
2996 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, 3137 _combinedSearchSelectionCancellationToken = _linkedSearchCancellationTokenSource.Token;
2997 async text => { await BeginSearch(text); }, _formTaskScheduler, 3138  
2998 _combinedSearchSelectionCancellationToken); 3139 _searchScheduledContinuation.Schedule(TimeSpan.FromMilliseconds(250), text, async text => { await BeginSearch(text); }, _formTaskScheduler, _combinedSearchSelectionCancellationToken);
-   3140 }
-   3141  
-   3142 private void PreviewFormClosing(object sender, CancelEventArgs e)
Line 2999... Line 3143...
2999 } 3143 {
3000   3144 if (_previewForm == null)
3001 private void PreviewFormClosing(object sender, CancelEventArgs e) 3145 {
3002 { 3146 return;
Line 3018... Line 3162...
3018 private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) 3162 private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
3019 { 3163 {
3020 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray(); 3164 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray();
Line 3021... Line 3165...
3021   3165  
3022 var item = items.FirstOrDefault(); 3166 var item = items.FirstOrDefault();
-   3167 if (item == null)
-   3168 {
-   3169 return;
Line 3023... Line 3170...
3023 if (item == null) return; 3170 }
3024   3171  
Line 3025... Line 3172...
3025 Process.Start("explorer.exe", $"/select, \"{item.Name}\""); 3172 Process.Start("explorer.exe", $"/select, \"{item.Name}\"");
3026 } 3173 }
3027   3174  
-   3175 private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
-   3176 {
-   3177 if (_aboutForm != null)
Line 3028... Line 3178...
3028 private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) 3178 {
3029 { 3179 return;
3030 if (_aboutForm != null) return; 3180 }
3031   3181  
Line 3213... Line 3363...
3213 return; 3363 return;
3214 } 3364 }
Line 3215... Line 3365...
3215   3365  
Line 3216... Line 3366...
3216 var item = info.Item; 3366 var item = info.Item;
-   3367  
-   3368 if (item == null)
-   3369 {
Line 3217... Line 3370...
3217   3370 return;
-   3371 }
3218 if (item == null) return; 3372  
3219   3373 if (_previewForm != null)
3220 if (_previewForm != null) 3374 {
3221 _previewForm.InvokeIfRequired(form => 3375 _previewForm.InvokeIfRequired(form =>
3222 { 3376 {
-   3377 form.Close();
Line 3223... Line 3378...
3223 form.Close(); 3378 form = null;
3224 form = null; 3379 });
3225 }); 3380 }
3226   3381  
Line 3244... Line 3399...
3244 var items = imageListView.SelectedItems.OfType<ListViewItem>(); 3399 var items = imageListView.SelectedItems.OfType<ListViewItem>();
Line 3245... Line 3400...
3245   3400  
3246 SelectTags(items); 3401 SelectTags(items);
Line 3247... Line 3402...
3247 } 3402 }
3248   3403  
3249 private async void imageListView_KeyUp(object sender, KeyEventArgs e) 3404 private void imageListView_KeyUp(object sender, KeyEventArgs e)
3250 { 3405 {
3251 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray(); 3406 var items = imageListView.SelectedItems.OfType<ListViewItem>().ToArray();
3252 if (e.KeyCode == Keys.Delete) 3407 if (e.KeyCode == Keys.Delete)
3253 { 3408 {
3254 await RemoveImagesAsync(items, _cancellationToken); 3409 RemoveImages(items, _cancellationToken);
Line 3255... Line 3410...
3255 return; 3410 return;
3256 } 3411 }
Line 3263... Line 3418...
3263 toolStripProgressBar1.Style = ProgressBarStyle.Continuous; 3418 toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
3264 toolStripProgressBar1.Minimum = 0; 3419 toolStripProgressBar1.Minimum = 0;
3265 toolStripProgressBar1.Value = 0; 3420 toolStripProgressBar1.Value = 0;
3266 toolStripProgressBar1.Maximum = 3; 3421 toolStripProgressBar1.Maximum = 3;
Line 3267... Line 3422...
3267   3422  
3268 var inputBlock = new BufferBlock<ListViewItem>(new DataflowBlockOptions -  
3269 { CancellationToken = _cancellationToken }); 3423 var inputBlock = new BufferBlock<ListViewItem>(new DataflowBlockOptions { CancellationToken = _cancellationToken });
3270 var fileTransformBlock = 3424 var fileTransformBlock =
3271 new TransformBlock<ListViewItem, (string Source, string Path, string Name, string Mime, string Extension -  
3272 )>(async item => 3425 new TransformBlock<ListViewItem, (string Source, string Path, string Name, string Mime, string Extension)>(async item =>
3273 { 3426 {
Line 3274... Line 3427...
3274 var mime = await _magicMime.GetMimeType(item.Name, _cancellationToken); 3427 var mime = await _magicMime.GetMimeType(item.Name, _cancellationToken);
3275   3428  
Line 3279... Line 3432...
3279 if (Configuration.OutboundDragDrop.RenameOnDragDrop) 3432 if (Configuration.OutboundDragDrop.RenameOnDragDrop)
3280 { 3433 {
3281 switch (Configuration.OutboundDragDrop.DragDropRenameMethod) 3434 switch (Configuration.OutboundDragDrop.DragDropRenameMethod)
3282 { 3435 {
3283 case DragDropRenameMethod.Random: 3436 case DragDropRenameMethod.Random:
3284 file = string.Join("", -  
3285 Enumerable.Repeat(0, 5).Select(n => (char)_random.Next('a', 'z' + 1))); 3437 file = string.Join("", Enumerable.Repeat(0, 5).Select(n => (char)_random.Next('a', 'z' + 1)));
3286 break; 3438 break;
3287 case DragDropRenameMethod.Timestamp: 3439 case DragDropRenameMethod.Timestamp:
3288 file = $"{DateTimeOffset.Now.ToUnixTimeSeconds()}"; 3440 file = $"{DateTimeOffset.Now.ToUnixTimeSeconds()}";
3289 break; 3441 break;
3290 } 3442 }
Line 3305... Line 3457...
3305 toolStripStatusLabel1.Text = "All files scanned for drag and drop."; 3457 toolStripStatusLabel1.Text = "All files scanned for drag and drop.";
3306 toolStripProgressBar1.Increment(1); 3458 toolStripProgressBar1.Increment(1);
3307 }, _formTaskScheduler); 3459 }, _formTaskScheduler);
Line 3308... Line 3460...
3308   3460  
3309 var noConvertTransformBlock = 3461 var noConvertTransformBlock =
3310 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>( -  
3311 async item => 3462 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>(async item =>
3312 { 3463 {
-   3464 var destination = Path.Combine(item.Path, $"{item.Name}{item.Extension}");
3313 var destination = Path.Combine(item.Path, $"{item.Name}{item.Extension}"); 3465  
Line 3314... Line 3466...
3314 await Miscellaneous.CopyFileAsync(item.Source, destination, _cancellationToken); 3466 await Miscellaneous.CopyFileAsync(item.Source, destination, _cancellationToken);
3315   3467  
3316 this.InvokeIfRequired(form => 3468 this.InvokeIfRequired(form =>
Line 3328... Line 3480...
3328 toolStripStatusLabel1.Text = "Conversion complete for drag and drop."; 3480 toolStripStatusLabel1.Text = "Conversion complete for drag and drop.";
3329 toolStripProgressBar1.Increment(1); 3481 toolStripProgressBar1.Increment(1);
3330 }, _formTaskScheduler); 3482 }, _formTaskScheduler);
Line 3331... Line 3483...
3331   3483  
3332 var jpegTransformBlock = 3484 var jpegTransformBlock =
3333 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>( -  
3334 async file => 3485 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>(async file =>
3335 { 3486 {
3336 using var imageStream = 3487 using var imageStream =
Line 3337... Line 3488...
3337 await _imageTool.ConvertTo(file.Source, MagickFormat.Jpeg, _cancellationToken); 3488 await _imageTool.ConvertTo(file.Source, MagickFormat.Jpeg, _cancellationToken);
3338   3489  
3339 var jpegDestination = Path.Combine(file.Path, $"{file.Name}.jpg"); -  
Line 3340... Line 3490...
3340 await Miscellaneous.CopyFileAsync(imageStream, jpegDestination, 3490 var jpegDestination = Path.Combine(file.Path, $"{file.Name}.jpg");
3341 _cancellationToken); 3491 await Miscellaneous.CopyFileAsync(imageStream, jpegDestination, _cancellationToken);
3342   3492  
3343 this.InvokeIfRequired(form => 3493 this.InvokeIfRequired(form =>
Line 3356... Line 3506...
3356 toolStripStatusLabel1.Text = "Conversion complete for drag and drop."; 3506 toolStripStatusLabel1.Text = "Conversion complete for drag and drop.";
3357 toolStripProgressBar1.Increment(1); 3507 toolStripProgressBar1.Increment(1);
3358 }, _formTaskScheduler); 3508 }, _formTaskScheduler);
Line 3359... Line 3509...
3359   3509  
3360 var pngTransformBlock = 3510 var pngTransformBlock =
3361 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>( -  
3362 async file => 3511 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>(async file =>
3363 { -  
3364 using var imageStream = 3512 {
Line 3365... Line 3513...
3365 await _imageTool.ConvertTo(file.Source, MagickFormat.Png, _cancellationToken); 3513 using var imageStream = await _imageTool.ConvertTo(file.Source, MagickFormat.Png, _cancellationToken);
3366   3514  
Line 3367... Line 3515...
3367 var pngDestination = Path.Combine(file.Path, $"{file.Name}.png"); 3515 var pngDestination = Path.Combine(file.Path, $"{file.Name}.png");
Line 3383... Line 3531...
3383 toolStripStatusLabel1.Text = "Conversion complete for drag and drop."; 3531 toolStripStatusLabel1.Text = "Conversion complete for drag and drop.";
3384 toolStripProgressBar1.Increment(1); 3532 toolStripProgressBar1.Increment(1);
3385 }, _formTaskScheduler); 3533 }, _formTaskScheduler);
Line 3386... Line 3534...
3386   3534  
3387 var bmpTransformBlock = 3535 var bmpTransformBlock =
3388 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>( -  
3389 async file => 3536 new TransformBlock<(string Source, string Path, string Name, string Mime, string Extension), string>( async file =>
3390 { -  
3391 using var imageStream = 3537 {
Line 3392... Line 3538...
3392 await _imageTool.ConvertTo(file.Source, MagickFormat.Jpeg, _cancellationToken); 3538 using var imageStream = await _imageTool.ConvertTo(file.Source, MagickFormat.Jpeg, _cancellationToken);
3393   3539  
Line 3394... Line 3540...
3394 var bmpDestination = Path.Combine(file.Path, $"{file.Name}.bmp"); 3540 var bmpDestination = Path.Combine(file.Path, $"{file.Name}.bmp");
Line 3414... Line 3560...
3414 var iptcStripTransformBlock = new TransformBlock<string, string>(async file => 3560 var iptcStripTransformBlock = new TransformBlock<string, string>(async file =>
3415 { 3561 {
3416 try 3562 try
3417 { 3563 {
3418 var mime = await _magicMime.GetMimeType(file, _cancellationToken); 3564 var mime = await _magicMime.GetMimeType(file, _cancellationToken);
3419   -  
3420 if (Configuration.SupportedFormats.IsSupportedVideo(mime)) 3565 if (Configuration.SupportedFormats.IsSupportedVideo(mime))
3421 { 3566 {
3422 return file; 3567 return file;
3423 } 3568 }
Line 3537... Line 3682...
3537 await Task.WhenAll( 3682 await Task.WhenAll(
3538 jpegTransformBlock.Completion, 3683 jpegTransformBlock.Completion,
3539 pngTransformBlock.Completion, 3684 pngTransformBlock.Completion,
3540 bmpTransformBlock.Completion, 3685 bmpTransformBlock.Completion,
3541 iptcStripTransformBlock.Completion, 3686 iptcStripTransformBlock.Completion,
3542 noConvertTransformBlock.Completion).ContinueWith(_ => { 3687 noConvertTransformBlock.Completion).ContinueWith(_ =>
-   3688 {
3543 outputBlock.Complete(); 3689 outputBlock.Complete();
3544 }, _cancellationToken); 3690 }, _cancellationToken);
Line 3545... Line 3691...
3545   3691  
3546 var files = new HashSet<string>(); 3692 var files = new HashSet<string>();
Line 3565... Line 3711...
3565 { 3711 {
3566 return; 3712 return;
3567 } 3713 }
Line 3568... Line 3714...
3568   3714  
3569 var data = new DataObject(DataFormats.FileDrop, files.ToArray()); 3715 var data = new DataObject(DataFormats.FileDrop, files.ToArray());
-   3716 this.InvokeIfRequired(_ =>
3570 this.InvokeIfRequired(_ => { 3717 {
3571 DoDragDrop(data, DragDropEffects.Copy); 3718 DoDragDrop(data, DragDropEffects.Copy);
3572 }); 3719 });
Line 3573... Line 3720...
3573 } 3720 }
Line 3603... Line 3750...
3603 AddToMostRecentlyUsedList = true, 3750 AddToMostRecentlyUsedList = true,
3604 Multiselect = true, 3751 Multiselect = true,
3605 IsFolderPicker = false 3752 IsFolderPicker = false
3606 }; 3753 };
Line -... Line 3754...
-   3754  
-   3755 switch (dialog.ShowDialog())
3607   3756 {
3608 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 3757 case CommonFileDialogResult.Ok:
-   3758 await LoadFilesAsync(dialog.FileNames, _magicMime, _cancellationToken);
-   3759 break;
3609 await LoadFilesAsync(dialog.FileNames, _magicMime, _cancellationToken); 3760 }
Line 3610... Line 3761...
3610 } 3761 }
3611   3762  
3612 private async void importDirectoryToolStripMenuItem_Click(object sender, EventArgs e) 3763 private async void importDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
Line 3616... Line 3767...
3616 AddToMostRecentlyUsedList = true, 3767 AddToMostRecentlyUsedList = true,
3617 Multiselect = true, 3768 Multiselect = true,
3618 IsFolderPicker = true 3769 IsFolderPicker = true
3619 }; 3770 };
Line -... Line 3771...
-   3771  
-   3772 switch (dialog.ShowDialog())
3620   3773 {
3621 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 3774 case CommonFileDialogResult.Ok:
-   3775 await LoadFilesAsync(dialog.FileNames, _magicMime, _cancellationToken);
-   3776 break;
3622 await LoadFilesAsync(dialog.FileNames, _magicMime, _cancellationToken); 3777 }
Line 3623... Line 3778...
3623 } 3778 }
3624   3779  
3625 private void settingsToolStripMenuItem_Click(object sender, EventArgs e) 3780 private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
-   3781 {
-   3782 if (_settingsForm != null)
-   3783 {
Line 3626... Line 3784...
3626 { 3784 return;
3627 if (_settingsForm != null) return; 3785 }
3628   3786  
3629 _settingsForm = new SettingsForm(Configuration, _cancellationToken); 3787 _settingsForm = new SettingsForm(Configuration, _cancellationToken);
Line 3630... Line 3788...
3630 _settingsForm.Closing += SettingsForm_Closing; 3788 _settingsForm.Closing += SettingsForm_Closing;
3631 _settingsForm.Show(); 3789 _settingsForm.Show();
3632 } 3790 }
-   3791  
-   3792 private void SettingsForm_Closing(object sender, CancelEventArgs e)
-   3793 {
Line 3633... Line 3794...
3633   3794 if (_settingsForm == null)
-   3795 {
3634 private void SettingsForm_Closing(object sender, CancelEventArgs e) 3796 return;
3635 { 3797 }
3636 if (_settingsForm == null) return; 3798  
-   3799 if (_settingsForm.SaveOnClose)
Line 3637... Line 3800...
3637   3800 {
3638 if (_settingsForm.SaveOnClose) 3801 // Commit the configuration.
3639 // Commit the configuration. 3802 _changedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
3640 _changedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 3803 async () => { await SaveConfiguration(Configuration); }, _cancellationToken);
Line 3705... Line 3868...
3705 private async void balanceTagsToolStripMenuItem_Click(object sender, EventArgs e) 3868 private async void balanceTagsToolStripMenuItem_Click(object sender, EventArgs e)
3706 { 3869 {
3707 var items = imageListView.SelectedItems.OfType<ListViewItem>(); 3870 var items = imageListView.SelectedItems.OfType<ListViewItem>();
Line 3708... Line 3871...
3708   3871  
3709 var listViewItems = items as ListViewItem[] ?? items.ToArray(); 3872 var listViewItems = items as ListViewItem[] ?? items.ToArray();
-   3873 if (listViewItems.Length < 2)
-   3874 {
-   3875 return;
Line 3710... Line 3876...
3710 if (listViewItems.Length < 2) return; 3876 }
3711   3877  
Line 3712... Line 3878...
3712 await BalanceTags(listViewItems); 3878 await BalanceTags(listViewItems);
Line 3725... Line 3891...
3725 { 3891 {
3726 var menuItem = (ToolStripMenuItem)sender; 3892 var menuItem = (ToolStripMenuItem)sender;
Line 3727... Line 3893...
3727   3893  
3728 foreach (var group in _imageListViewGroupDictionary.Keys) 3894 foreach (var group in _imageListViewGroupDictionary.Keys)
3729 { 3895 {
-   3896 if (menuItem.DropDownItems.ContainsKey(group))
-   3897 {
-   3898 continue;
Line 3730... Line 3899...
3730 if (menuItem.DropDownItems.ContainsKey(group)) continue; 3899 }
3731   3900  
3732 var toolStripMenuSubItem = new ToolStripButton(group) { Name = group }; 3901 var toolStripMenuSubItem = new ToolStripButton(group) { Name = group };
3733 toolStripMenuSubItem.Click += moveTargetToolStripMenuItem_Click; 3902 toolStripMenuSubItem.Click += moveTargetToolStripMenuItem_Click;
-   3903 menuItem.DropDownItems.Add(toolStripMenuSubItem);
-   3904 }
-   3905 }
-   3906  
-   3907 private void moveToolStripMenuItem_DropDownClosed(object sender, EventArgs e)
-   3908 {
-   3909 var menuItem = (ToolStripMenuItem)sender;
-   3910  
-   3911 var items = new ConcurrentBag<ToolStripButton>();
-   3912 foreach(var toolStripMenuSubItem in menuItem.DropDownItems.OfType<ToolStripButton>())
-   3913 {
-   3914 toolStripMenuSubItem.Click -= moveTargetToolStripMenuItem_Click;
-   3915  
-   3916 items.Add(toolStripMenuSubItem);
-   3917 }
-   3918  
-   3919 foreach(var toolStripMenuSubItem in items)
-   3920 {
3734 menuItem.DropDownItems.Add(toolStripMenuSubItem); 3921 menuItem.DropDownItems.Remove(toolStripMenuSubItem);
Line 3735... Line 3922...
3735 } 3922 }
3736 } 3923 }
3737   3924  
Line 3769... Line 3956...
3769 { "png", "image/png" }, 3956 { "png", "image/png" },
3770 { "bmp", "image/bmp" }, 3957 { "bmp", "image/bmp" },
3771 { "gif", "image/gif" } 3958 { "gif", "image/gif" }
3772 }; 3959 };
Line 3773... Line 3960...
3773   3960  
-   3961 if (!Configuration.SupportedFormats.Images.Image.Contains(extensionToMime[extension]))
-   3962 {
-   3963 return;
Line 3774... Line 3964...
3774 if (!Configuration.SupportedFormats.Images.Image.Contains(extensionToMime[extension])) return; 3964 }
Line 3775... Line 3965...
3775   3965  
3776 var items = imageListView.SelectedItems.OfType<ListViewItem>(); 3966 var items = imageListView.SelectedItems.OfType<ListViewItem>();