/// <summary> /// Add user interface event handler callbacks for (possibly invisible) controls /// </summary> private void SetUserInterfaceCallbacks() { // Add data entry callbacks to all editable controls. When the user changes an image's attribute using a particular control, // the callback updates the matching field for that image in the database. DataEntryNote date = null; DataEntryDateTime dateTime = null; DataEntryNote time = null; foreach (KeyValuePair <string, DataEntryControl> pair in this.DataEntryControls.ControlsByDataLabel) { string controlType = this.DataHandler.FileDatabase.FileTableColumnsByDataLabel[pair.Key].ControlType; switch (controlType) { case Constant.Control.Counter: DataEntryCounter counter = (DataEntryCounter)pair.Value; counter.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; counter.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; counter.Container.MouseEnter += this.CounterControl_MouseEnter; counter.Container.MouseLeave += this.CounterControl_MouseLeave; counter.LabelControl.Click += this.CounterControl_Click; break; case Constant.Control.Flag: case Constant.DatabaseColumn.DeleteFlag: DataEntryFlag flag = (DataEntryFlag)pair.Value; flag.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; flag.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; break; case Constant.Control.FixedChoice: case Constant.DatabaseColumn.ImageQuality: DataEntryChoice choice = (DataEntryChoice)pair.Value; choice.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; choice.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; break; case Constant.Control.Note: case Constant.DatabaseColumn.Date: case Constant.DatabaseColumn.File: case Constant.DatabaseColumn.Folder: case Constant.DatabaseColumn.RelativePath: case Constant.DatabaseColumn.Time: DataEntryNote note = (DataEntryNote)pair.Value; note.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; note.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; if (controlType == Constant.DatabaseColumn.Date) { date = note; } if (controlType == Constant.DatabaseColumn.Time) { time = note; } break; case Constant.DatabaseColumn.DateTime: dateTime = (DataEntryDateTime)pair.Value; dateTime.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; dateTime.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; break; case Constant.DatabaseColumn.UtcOffset: DataEntryUtcOffset utcOffset = (DataEntryUtcOffset)pair.Value; utcOffset.ContentControl.PreviewMouseDown += this.ContentControl_MouseDown; utcOffset.ContentControl.PreviewKeyDown += this.ContentCtl_PreviewKeyDown; break; default: TracePrint.PrintMessage(String.Format("Unhandled control type '{0}' in SetUserInterfaceCallbacks.", controlType)); break; } } // if needed, link date and time controls to datetime control if (dateTime != null && date != null) { dateTime.DateControl = date; } if (dateTime != null && time != null) { dateTime.TimeControl = time; } }
public void CreateControls(FileDatabase database, DataEntryHandler dataEntryPropagator) { // Depending on how the user interacts with the file import process image set loading can be aborted after controls are generated and then // another image set loaded. Any existing controls therefore need to be cleared. this.ControlGrid.Children.Clear(); this.Controls.Clear(); this.ControlsByDataLabel.Clear(); DataEntryDateTime dateTimeControl = null; DataEntryUtcOffset utcOffsetControl = null; List<DataEntryControl> visibleControls = new List<DataEntryControl>(); foreach (ControlRow control in database.Controls) { // no point in generating a control if it doesn't render in the UX if (control.Visible == false) { continue; } if (control.Type == Constant.DatabaseColumn.DateTime) { dateTimeControl = new DataEntryDateTime(control, this); visibleControls.Add(dateTimeControl); } else if (control.Type == Constant.DatabaseColumn.File || control.Type == Constant.DatabaseColumn.RelativePath || control.Type == Constant.Control.Note) { // standard controls rendering as notes aren't editable by the user List<string> autocompletions = null; bool readOnly = control.Type != Constant.Control.Note; if (readOnly == false) { autocompletions = new List<string>(database.GetDistinctValuesInFileDataColumn(control.DataLabel)); } DataEntryNote noteControl = new DataEntryNote(control, autocompletions, this); noteControl.ContentReadOnly = readOnly; visibleControls.Add(noteControl); } else if (control.Type == Constant.Control.Flag || control.Type == Constant.DatabaseColumn.DeleteFlag) { DataEntryFlag flagControl = new DataEntryFlag(control, this); visibleControls.Add(flagControl); } else if (control.Type == Constant.Control.Counter) { DataEntryCounter counterControl = new DataEntryCounter(control, this); visibleControls.Add(counterControl); } else if (control.Type == Constant.Control.FixedChoice || control.Type == Constant.DatabaseColumn.ImageQuality) { DataEntryChoice choiceControl = new DataEntryChoice(control, this); visibleControls.Add(choiceControl); } else if (control.Type == Constant.DatabaseColumn.UtcOffset) { utcOffsetControl = new DataEntryUtcOffset(control, this); visibleControls.Add(utcOffsetControl); } else { Debug.Fail(String.Format("Unhandled control type {0}.", control.Type)); continue; } } if ((dateTimeControl != null) && (utcOffsetControl != null)) { dateTimeControl.ShowUtcOffset(); visibleControls.Remove(utcOffsetControl); } foreach (DataEntryControl control in visibleControls) { this.ControlGrid.Children.Add(control.Container); this.Controls.Add(control); this.ControlsByDataLabel.Add(control.DataLabel, control); } dataEntryPropagator.SetDataEntryCallbacks(this.ControlsByDataLabel); }
// Show the image / video file for the specified row, but only if its different from what is currently being displayed. private void FileShow(int fileIndex, bool isInSliderNavigation, bool forceUpdate) { // If there is no image set open, or if there is no image to show, then show an image indicating the empty image set. if (this.IsFileDatabaseAvailable() == false || this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles < 1) { this.MarkableCanvas.SetNewImage(Constant.ImageValues.NoFilesAvailable.Value, null); this.markersOnCurrentFile = null; this.MarkableCanvas_UpdateMarkers(); this.MarkableCanvas.SwitchToImageView(); // We could invalidate the cache here, but it will be reset anyways when images are loaded. if (this.DataHandler != null) { this.DataHandler.IsProgrammaticControlUpdate = false; } // We also need to do a bit of cleanup of UI elements that make no sense when there are no images to show. this.QuickPasteWindowHide(); return; } // If we are already showing the desired file, and if we are not forcing an update, // then abort as there is no need to redisplay the image. if (this.DataHandler.ImageCache.CurrentRow == fileIndex && forceUpdate == false) { return; } this.DataEntryControls.AutocompletionUpdateWithCurrentRowValues(); // for the bitmap caching logic below to work this should be the only place where code in TimelapseWindow moves the image enumerator if (this.DataHandler.ImageCache.TryMoveToFile(fileIndex, forceUpdate, out bool newFileToDisplay) == false) { if (this.DataHandler != null) { this.DataHandler.IsProgrammaticControlUpdate = false; } // We used to throw a new exception, but lets see what happens if we just return instead. // i.e., lets just abort. // throw new Exception(String.Format("in FileShow: possible problem with fileIndex value is {0}, where its not a valid row index in the image table.", fileIndex)); System.Diagnostics.Debug.Print(String.Format("in FileShow: possible problem with fileIndex (value is {0}, where its not a valid row index in the image table.", fileIndex)); return; } // Reset the ThumbnailGrid to the current image // SAULXX: COULD SET FOLDER PATH AND FILEDATABASE ON LOAD, BUT MAY BE BETTER TO JUST KEEP ON DOING IT HERE // SAULXX: Note that this used to be before the above if statement. Not sure if it would be a problem having it here (in case of failur) this.MarkableCanvas.ThumbnailGrid.FolderPath = this.FolderPath; this.MarkableCanvas.ThumbnailGrid.FileTableStartIndex = fileIndex; this.MarkableCanvas.ThumbnailGrid.FileTable = this.DataHandler.FileDatabase.FileTable; // Update each control with the data for the now current image // This is always done as it's assumed either the image changed or that a control refresh is required due to database changes // the call to TryMoveToImage() above refreshes the data stored under this.dataHandler.ImageCache.Current. this.DataHandler.IsProgrammaticControlUpdate = true; foreach (KeyValuePair <string, DataEntryControl> control in this.DataEntryControls.ControlsByDataLabel) { // update value string controlType = this.DataHandler.FileDatabase.FileTableColumnsByDataLabel[control.Key].ControlType; control.Value.SetContentAndTooltip(this.DataHandler.ImageCache.Current.GetValueDisplayString(control.Value.DataLabel)); // for note controls, update the autocomplete list if an edit occurred if (controlType == Constant.Control.Note) { DataEntryNote noteControl = (DataEntryNote)control.Value; if (noteControl.ContentChanged) { noteControl.ContentChanged = false; } } } this.DataHandler.IsProgrammaticControlUpdate = false; // update the status bar to show which image we are on out of the total displayed under the current selection // the total is always refreshed as it's not known if FileShow() is being called due to a change in the selection this.StatusBar.SetCurrentFile(fileIndex + 1); // Add one because indexes are 0-based this.StatusBar.SetCount(this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles); this.StatusBar.ClearMessage(); this.FileNavigatorSlider.Value = fileIndex + 1; // Get the bounding boxes and markers (if any) for the current image; BoundingBoxes bboxes = this.GetBoundingBoxesForCurrentFile(this.DataHandler.ImageCache.Current.ID); this.markersOnCurrentFile = this.DataHandler.FileDatabase.MarkersGetMarkersForCurrentFile(this.DataHandler.ImageCache.Current.ID); List <Marker> displayMarkers = this.GetDisplayMarkers(); // Display new file if the file changed // This avoids unnecessary image reloads and refreshes in cases where FileShow() is just being called to refresh controls if (newFileToDisplay) { if (this.DataHandler.ImageCache.Current.IsVideo) { this.MarkableCanvas.SetNewVideo(this.DataHandler.ImageCache.Current.GetFileInfo(this.DataHandler.FileDatabase.FolderPath), displayMarkers); this.EnableImageManipulationMenus(false); } else { this.MarkableCanvas.SetNewImage(this.DataHandler.ImageCache.GetCurrentImage, displayMarkers); // Draw markers for this file this.MarkableCanvas_UpdateMarkers(); this.MarkableCanvas.BoundingBoxes = bboxes; this.EnableImageManipulationMenus(true); } } else if (this.IsDisplayingSingleImage()) { if (this.DataHandler.ImageCache.Current.IsVideo) { this.MarkableCanvas.SwitchToVideoView(); } else { this.MarkableCanvas.SwitchToImageView(); this.MarkableCanvas_UpdateMarkers(); } } this.DataGridSelectionsTimer_Reset(); // Set the file player status if (this.DataHandler.ImageCache.CurrentRow == 0) { this.FilePlayer.BackwardsControlsEnabled(false); } else { this.FilePlayer.BackwardsControlsEnabled(true); } if (this.DataHandler.ImageCache.CurrentRow == this.DataHandler.FileDatabase.CountAllCurrentlySelectedFiles - 1) { this.FilePlayer.ForwardsControlsEnabled(false); } else { this.FilePlayer.ForwardsControlsEnabled(true); } // Refresh the Magnifier if needed if (this.IsDisplayingSingleImage()) { if (this.DataHandler.ImageCache.Current.IsVideo) { this.MarkableCanvas.SetMagnifiersAccordingToCurrentState(false, true); } else { this.MarkableCanvas.SetMagnifiersAccordingToCurrentState(true, false); } } // Refresh the CopyPreviousButton and its Previews as needed this.CopyPreviousValuesSetEnableStatePreviewsAndGlowsAsNeeded(); // Refresh the QuickPasteEntry previews if needed if (this.IsDisplayingSingleImage() && this.quickPasteWindow != null) { this.quickPasteWindow.RefreshQuickPasteWindowPreviewAsNeeded(); } // Refresh the markable canvas if needed this.MarkableCanvas.RefreshIfMultipleImagesAreDisplayed(isInSliderNavigation); // Display the episode and duplicate text as needed this.DisplayEpisodeTextInImageIfWarranted(fileIndex); this.DuplicateDisplayIndicatorInImageIfWarranted(); }