/// <summary> /// Get and display the episode text if various conditions are met /// </summary> private void DisplayEpisodeTextInImageIfWarranted(int fileIndex) { if (Episodes.ShowEpisodes && this.IsDisplayingSingleImage()) { if (Episodes.EpisodesDictionary.ContainsKey(fileIndex) == false) { Episodes.EpisodeGetEpisodesInRange(this.DataHandler.FileDatabase.FileTable, this.DataHandler.ImageCache.CurrentRow); } Tuple <int, int> episode = Episodes.EpisodesDictionary[fileIndex]; if (episode.Item1 == int.MaxValue) { this.EpisodeText.Text = "Episode \u221E"; } else { this.EpisodeText.Text = (episode.Item2 == 1) ? "Single" : String.Format("Episode {0}/{1}", episode.Item1, episode.Item2); } this.EpisodeText.Foreground = (episode.Item1 == 1) ? Brushes.Red : Brushes.Black; this.EpisodeText.Visibility = Visibility.Visible; } else { this.EpisodeText.Visibility = Visibility.Hidden; } }
public static void EpisodeGetEpisodesInRange(FileTable fileTable, int fileTableIndex, int maxRangeToSearch) { if (Episodes.EpisodesDictionary == null) { Episodes.Reset(); } int index = fileTableIndex; // Ensure the argument is valid if (fileTable == null || index < 0 || index >= fileTable.RowCount) { return; } bool inRange = Episodes.EpisodeGetAroundIndex(fileTable, fileTableIndex, maxRangeToSearch, out int first, out int count); // foreach fileindex within the episode, ranging from first to last, add its episode information to the episode dictionary for (int i = 1; i <= count; i++) { int currentFileIndex = first + i - 1; if (!Episodes.EpisodesDictionary.ContainsKey(currentFileIndex)) { Tuple <int, int> tuple = inRange ? new Tuple <int, int>(i, count) : new Tuple <int, int>(int.MaxValue, int.MaxValue); Episodes.EpisodesDictionary.Add(currentFileIndex, tuple); } } }
// View Episode helper function private void EpisodeShowNextOrPrevious_Click(DirectionEnum direction) { long currentFileID = this.DataHandler.ImageCache.Current.ID; bool result = Episodes.GetIncrementToNextEpisode(this.DataHandler.FileDatabase.FileTable, this.DataHandler.FileDatabase.GetFileOrNextFileIndex(currentFileID), direction, out int increment); if (result == true) { if (Episodes.ShowEpisodes == false) { // turn on Episode display if its not already on this.EpisodeShowHide(true); } // At this point, the episodes should be showing and the increment amount should be reset (see the out parameter above) this.TryFileShowWithoutSliderCallback(direction, increment); } }
private void MenuItemEpisodeOptions_Click(object sender, RoutedEventArgs e) { EpisodeOptions episodeOptions = new EpisodeOptions(this.State.EpisodeTimeThreshold, this); bool? result = episodeOptions.ShowDialog(); if (result == true) { // the time threshold has changed, so save its new state this.State.EpisodeTimeThreshold = episodeOptions.EpisodeTimeThreshold; Episodes.TimeThreshold = this.State.EpisodeTimeThreshold; // so we don't have to pass it as a parameter Episodes.Reset(); } if (this.IsDisplayingMultipleImagesInOverview()) { this.MarkableCanvas.RefreshIfMultipleImagesAreDisplayed(false); } else { this.DisplayEpisodeTextInImageIfWarranted(this.DataHandler.ImageCache.CurrentRow); } }
// PEFORMANCE FILES SELECT AND SHOW CALLED TOO OFTEN, GIVEN THAT IT IS A SLOW OPERATION private async Task FilesSelectAndShowAsync(long imageID, FileSelectionEnum selection) { // change selection // if the data grid is bound the file database automatically updates its contents on SelectFiles() if (this.DataHandler == null) { TracePrint.PrintMessage("FilesSelectAndShow() should not be reachable with a null data handler. Is a menu item wrongly enabled?");; } if (this.DataHandler.FileDatabase == null) { TracePrint.PrintMessage("FilesSelectAndShow() should not be reachable with a null database. Is a menu item wrongly enabled?"); } // Select the files according to the given selection // Note that our check for missing actually checks to see if the file exists, // which is why its a different operation // PEFORMANCE - TWO SLOW OPERATIONS: FilesSelectAndShow invoking this.dataHandler.FileDatabase.SelectFile / .SelectMissingFilesFromCurrentlySelectedFiles Mouse.OverrideCursor = Cursors.Wait; if (selection == FileSelectionEnum.Missing) { // PERFORMANCE this can be slow if there are many files, as it checks every single file in the current database selection to see if it exists // However, it is not a mainstream operation so can be considered a lower priority place for optimization bool anyFilesMissing = this.DataHandler.FileDatabase.SelectMissingFilesFromCurrentlySelectedFiles(); if (anyFilesMissing == false) { // No files were missing. Inform the user, and don't change anything. Mouse.OverrideCursor = null; Dialogs.FileSelectionNoFilesAreMissingDialog(this); return; } } else { // If its a folder selection, record it so we can save it later in the image set table this.DataHandler.FileDatabase.ImageSet.SelectedFolder = selection == FileSelectionEnum.Folders ? this.DataHandler.FileDatabase.GetSelectedFolder() : String.Empty; // PERFORMANCE Select Files is a very slow operation as it runs a query over all files and returns everything it finds as datatables stored in memory. this.BusyCancelIndicator.EnableForSelection(true); await this.DataHandler.FileDatabase.SelectFilesAsync(selection).ConfigureAwait(true); this.BusyCancelIndicator.EnableForSelection(false); this.DataHandler.FileDatabase.BindToDataGrid(); } Mouse.OverrideCursor = null; if ((this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles < 1) && (selection != FileSelectionEnum.All)) { // Tell the user that we are resetting the selection to all files Dialogs.FileSelectionResettngSelectionToAllFilesDialog(this, selection); this.StatusBar.SetMessage("Resetting selection to All files."); selection = FileSelectionEnum.All; // PEFORMANCE: The standard select files operation in FilesSelectAndShow this.BusyCancelIndicator.EnableForSelection(true); await this.DataHandler.FileDatabase.SelectFilesAsync(selection).ConfigureAwait(true); this.BusyCancelIndicator.EnableForSelection(false); this.DataHandler.FileDatabase.BindToDataGrid(); } // Change the selection to reflect what the user selected. Update the menu state accordingly // Set the checked status of the radio button menu items to the selection. string status; switch (selection) { case FileSelectionEnum.All: status = "All files"; break; case FileSelectionEnum.Custom: status = "Custom selection"; break; case FileSelectionEnum.Dark: status = "Dark files"; break; case FileSelectionEnum.MarkedForDeletion: status = "Files marked for deletion"; break; case FileSelectionEnum.Folders: status = "Files in a specific folder"; break; case FileSelectionEnum.Missing: status = "Missing files"; break; case FileSelectionEnum.Ok: status = "Non-dark files"; break; default: throw new NotSupportedException(String.Format("Unhandled file selection {0}.", selection)); } // Show feedback of the status description in both the status bar and the data entry control panel title this.StatusBar.SetView(status); this.DataEntryControlPanel.Title = "Data entry for " + status; // Reset the Episodes, as it may change based on the current selection Episodes.Reset(); // Display the specified file or, if it's no longer selected, the next closest one // FileShow() handles empty image sets, so those don't need to be checked for here. // After a selection changes, set the slider to represent the index and the count of the current selection this.FileNavigatorSlider_EnableOrDisableValueChangedCallback(false); this.FileNavigatorSlider.Maximum = this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles; // Reset the slider to the size of images in this set if (this.FileNavigatorSlider.Maximum <= 50) { this.FileNavigatorSlider.IsSnapToTickEnabled = true; this.FileNavigatorSlider.TickFrequency = 1.0; } else { this.FileNavigatorSlider.IsSnapToTickEnabled = false; this.FileNavigatorSlider.TickFrequency = 0.02 * this.FileNavigatorSlider.Maximum; } // Reset the ThumbnailGrid selection after every change in the selection if (this.IsDisplayingMultipleImagesInOverview()) { this.MarkableCanvas.ThumbnailGrid.SelectInitialCellOnly(); } this.DataEntryControls.AutocompletionPopulateAllNotesWithFileTableValues(this.DataHandler.FileDatabase); // Always force an update after a selection this.FileShow(this.DataHandler.FileDatabase.GetFileOrNextFileIndex(imageID), true); // Update the status bar accordingly this.StatusBar.SetCurrentFile(this.DataHandler.ImageCache.CurrentRow + 1); // We add 1 because its a 0-based list this.StatusBar.SetCount(this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles); this.FileNavigatorSlider_EnableOrDisableValueChangedCallback(true); this.DataHandler.FileDatabase.ImageSet.FileSelection = selection; // Remember the current selection }
public void Handle_PreviewKeyDown(KeyEventArgs currentKey, bool forceSendToMainWindow) { // Check the arguments for null ThrowIf.IsNullArgument(currentKey, nameof(currentKey)); // First, try to interpret key as a possible valid quickpaste shortcut key. // If so, send it to the Quickpaste window and mark the event as handled. if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && ((currentKey.Key >= Key.D0 && currentKey.Key <= Key.D9) || (currentKey.Key >= Key.NumPad0 && currentKey.Key <= Key.NumPad9))) { if (this.quickPasteWindow != null && this.quickPasteWindow.Visibility == Visibility.Visible) { // The quickpaste window is visible, and thus able to take shortcuts. string key = new KeyConverter().ConvertToString(currentKey.Key); if (key.StartsWith("NumPad")) { key = key.Remove(0, 6); } if (Int32.TryParse(key, out int shortcutIndex) && shortcutIndex != 0) { this.quickPasteWindow.TryQuickPasteShortcut(shortcutIndex); currentKey.Handled = true; } } return; } // Next, - but only if forceSendToMainWindow is true, // don't interpret keyboard shortcuts if the focus is on a control in the control grid, as the text entered may be directed // to the controls within it. That is, if a textbox or combo box has the focus, then take no as this is normal text input // and NOT a shortcut key. Similarly, if a menu is displayed keys should be directed to the menu rather than interpreted as // shortcuts. if (forceSendToMainWindow == false && this.SendKeyToDataEntryControlOrMenu(currentKey)) { return; } // Finally, test for other shortcut keys and take the appropriate action as needed DirectionEnum direction; int keyRepeatCount = this.State.GetKeyRepeatCount(currentKey); switch (currentKey.Key) { case Key.B: // Save a Bookmark of the current pan / zoom level of the image this.MarkableCanvas.SetBookmark(); break; case Key.Escape: this.TrySetKeyboardFocusToMarkableCanvas(false, currentKey); break; case Key.OemPlus: // Restore the zoom level / pan coordinates of the bookmark this.MarkableCanvas.ApplyBookmark(); break; case Key.OemMinus: // Restore the zoom level / pan coordinates of the bookmark this.MarkableCanvas.ZoomOutAllTheWay(); break; case Key.M: // Toggle the magnifying glass on and off this.MenuItemDisplayMagnifyingGlass_Click(this, null); break; case Key.U: // Increase the magnifing glass zoom level this.FilePlayer_Stop(); // In case the FilePlayer is going this.MarkableCanvas.MagnifierOrOffsetChangeZoomLevel(ZoomDirection.ZoomIn); break; case Key.D: // Decrease the magnifing glass zoom level this.FilePlayer_Stop(); // In case the FilePlayer is going this.MarkableCanvas.MagnifierOrOffsetChangeZoomLevel(ZoomDirection.ZoomOut); break; case Key.Right: // next /previous image case Key.Left: // previous image int increment = 1; this.FilePlayer_Stop(); // In case the FilePlayer is going direction = currentKey.Key == Key.Right ? DirectionEnum.Next : DirectionEnum.Previous; if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.LeftCtrl)) { long currentFileID = this.DataHandler.ImageCache.Current.ID; bool result = Episodes.GetIncrementToNextEpisode(this.DataHandler.FileDatabase.FileTable, this.DataHandler.FileDatabase.GetFileOrNextFileIndex(currentFileID), direction, out increment); if (result == true) { if (Episodes.ShowEpisodes == false) { // turn on Episode display if its not already on this.EpisodeShowHide(true); } // At this point, the episodes should be showing and the increment amount should be reset (see the out parameter above) } } if (currentKey.IsRepeat == false || (currentKey.IsRepeat == true && keyRepeatCount % this.State.Throttles.RepeatedKeyAcceptanceInterval == 0)) { this.TryFileShowWithoutSliderCallback(direction, increment); } break; case Key.Up: // show visual difference to next image if (this.IsDisplayingMultipleImagesInOverview()) { this.FilePlayer.Direction = DirectionEnum.Previous; this.FilePlayer_ScrollRow(); } else { this.FilePlayer_Stop(); // In case the FilePlayer is going this.TryViewPreviousOrNextDifference(); } break; case Key.Down: // show visual difference to previous image if (this.IsDisplayingMultipleImagesInOverview()) { this.FilePlayer.Direction = DirectionEnum.Next; this.FilePlayer_ScrollRow(); } else { this.FilePlayer_Stop(); // In case the FilePlayer is going this.TryViewCombinedDifference(); } break; case Key.C: if (this.State.IsViewOnly == false) { this.CopyPreviousValues_Click(); } break; case Key.E: this.MenuItemEpisodeShowHide_Click(null, null); break; case Key.Q: // Toggle the QuickPaste window if (this.quickPasteWindow == null || (this.quickPasteWindow.Visibility != Visibility.Visible)) { if (this.State.IsViewOnly == false) { this.QuickPasteWindowShow(); } } else { this.QuickPasteWindowHide(); } break; case Key.Tab: this.FilePlayer_Stop(); // In case the FilePlayer is going this.MoveFocusToNextOrPreviousControlOrCopyPreviousButton(Keyboard.Modifiers == ModifierKeys.Shift); break; case Key.PageDown: case Key.PageUp: direction = currentKey.Key == Key.PageDown ? DirectionEnum.Next : DirectionEnum.Previous; if (this.IsDisplayingMultipleImagesInOverview()) { this.FilePlayer.Direction = direction; this.FilePlayer_ScrollPage(); } else { this.FilePlayer_Stop(); // In case the FilePlayer is going if (currentKey.IsRepeat == false || (currentKey.IsRepeat == true && keyRepeatCount % this.State.Throttles.RepeatedKeyAcceptanceInterval == 0)) { this.TryFileShowWithoutSliderCallback(direction); } } break; case Key.Home: case Key.End: { this.DataGridPane.IsActive = true; break; } default: return; } if (currentKey != null) { currentKey.Handled = true; } }