public FeedbackRowTuple(string sampleFileName, string currentDate, string newDate, string numFilesWithThatDate, ImageRow imageRow, AmbiguousDateRange ambiguousDateRange) { // Properties that will be displayed in the data grid this.Select = false; this.SampleFileName = sampleFileName; this.CurrentDate = currentDate; this.NewDate = newDate; this.NumFilesWithThatDate = numFilesWithThatDate; this.ImageRow = imageRow; this.AmbiguousDateRange = ambiguousDateRange; }
public UpdateInfo(ImageRow row) : base(row) { }
public WsReference(ImageRow imageRow) : base(imageRow) { }
public ImageRowElement(ImageRow imageRow) : this() { ImageRow = imageRow; }
private void DeleteCurrentImageOnly() { // The single file to delete ImageRow imageRow = this.filesToDelete[0]; // Show the deleted file name and image in the interface this.ShowSingleFileView(); this.maxPathLength = 70; string filePath = Path.Combine(imageRow.RelativePath, imageRow.File); if (string.IsNullOrEmpty(filePath) == false) { filePath = filePath.Length <= this.maxPathLength ? filePath : "..." + filePath.Substring(filePath.Length - this.maxPathLength, this.maxPathLength); } this.SingleImageViewer.Source = imageRow.LoadBitmap(this.fileDatabase.FolderPath, Constant.ImageValues.PreviewWidth480, out _); this.SingleFilePanel.ToolTip = Path.Combine(imageRow.RelativePath, imageRow.File); this.SingleImageViewer.ToolTip = Path.Combine(imageRow.RelativePath, imageRow.File); this.SingleFileNameRun.Text = filePath; // Populate the information pane string imageOrVideo = this.filesToDelete[0].IsVideo ? "video" : "image"; if (this.deleteImage == true) { this.Message.Title = String.Format("Delete the current {0}", imageOrVideo); this.Message.What = String.Format("Deletes the current {0} if it exists", imageOrVideo); this.Message.Result = String.Format("\u2022 The deleted {0} will be backed up in a sub-folder named {1}.{2}", imageOrVideo, Constant.File.DeletedFilesFolder, Environment.NewLine); this.Message.Hint = String.Format("\u2022 Restore the deleted {0} by manually moving it ", imageOrVideo); if (this.deleteImage == true && this.deleteData == false) { // Case 1: Delete the current image, but not its data. this.Message.Title += " but not its data."; this.Message.What += String.Format("{0}The data entered for the {1} IS NOT deleted.", Environment.NewLine, imageOrVideo); this.Message.Result += String.Format("\u2022 A placeholder {0} will be shown when you try to view a deleted {0}.", imageOrVideo); this.Message.Hint += "back to its original location." + Environment.NewLine; } else if (this.deleteImage == true && this.deleteData == true) { // Case 2: Delete the current image and its data this.Message.Title += " and its data"; this.Message.What += String.Format("{0}The data entered for the {1} IS deleted as well.", Environment.NewLine, imageOrVideo); this.Message.Result += String.Format("\u2022 However, the data associated with that {0} will be permanently deleted.", imageOrVideo); this.Message.Hint += "to a new sub-folder." + Environment.NewLine + " Then add that sub-folder back to the image set." + Environment.NewLine; } this.Message.Hint += String.Format("\u2022 See Options|Preferences to manage how files in {0} are permanently deleted.", Constant.File.DeletedFilesFolder); } else { // Case: Delete the data only, leaving the image intact this.FileLabel.Text = "Affected file:"; this.ConfirmCheckBoxText.Text = "Click to confirm deletion of data for the selected file"; this.Message.Title = String.Format("Delete only the current {0}'s data", imageOrVideo); this.Message.What = String.Format("Deletes the data associated with the current {0}, but leaves the {0} intact", imageOrVideo); this.Message.Result = String.Format("\u2022 This data record will be removed.{0}", Environment.NewLine); this.Message.Result += String.Format("\u2022 The {0} is still intact, but it will not be displayed in Timelapse {1}", imageOrVideo, Environment.NewLine); this.Message.Result += String.Format(" unless a duplicate record exists.", imageOrVideo); this.Message.Hint = "Deleting only the data is useful for removing a previously-created duplicate record of a file."; } }
public ChartOfAccounts(ImageRow imageRow) : base(imageRow) { }
public Report(ImageRow imageRow) : base(imageRow) { }
public ChartOfCharacteristicTypes(ImageRow imageRow) : base(imageRow) { }
// Populate the database with the metadata for the selected note field private async Task <ObservableCollection <KeyValuePair <string, string> > > PopulateAsync(bool?metadataExtractorRBIsChecked) { // This list will hold key / value pairs that will be bound to the datagrid feedback, // which is the way to make those pairs appear in the data grid during background worker progress updates ObservableCollection <KeyValuePair <string, string> > keyValueList = new ObservableCollection <KeyValuePair <string, string> >(); return(await Task.Run(() => { // For each row in the database, get the image filename and try to extract the chosen metadata value. // If we can't decide if we want to leave the data field alone or to clear it depending on the state of the isClearIfNoMetadata (set via the checkbox) // Report progress as needed. // This tuple list will hold the id, key and value that we will want to update in the database string dataLabelToUpdate = this.dataLabelByLabel[this.dataFieldLabel]; List <ColumnTuplesWithWhere> imagesToUpdate = new List <ColumnTuplesWithWhere>(); TimeZoneInfo imageSetTimeZone = this.fileDatabase.ImageSet.GetSystemTimeZone(); int percentDone = 0; double totalImages = this.fileDatabase.CountAllCurrentlySelectedFiles; Dictionary <string, ImageMetadata> metadata = new Dictionary <string, ImageMetadata>(); for (int imageIndex = 0; imageIndex < totalImages; ++imageIndex) { // Provide feedback if the operation was cancelled during the database update if (Token.IsCancellationRequested == true) { keyValueList.Clear(); keyValueList.Add(new KeyValuePair <string, string>("Cancelled", "No changes were made")); return keyValueList; } ImageRow image = this.fileDatabase.FileTable[imageIndex]; if (metadataExtractorRBIsChecked == true) { // MetadataExtractor specific code metadata = ImageMetadataDictionary.LoadMetadata(image.GetFilePath(this.fileDatabase.FolderPath)); } else { // ExifTool specific code - note that we transform results into the same dictionary structure used by the MetadataExtractor string[] tags = { this.metadataFieldName }; metadata.Clear(); Dictionary <string, string> exifData = this.exifTool.FetchExifFrom(image.GetFilePath(this.fileDatabase.FolderPath), tags); if (exifData.ContainsKey(tags[0])) { metadata.Add(tags[0], new Timelapse.Util.ImageMetadata(String.Empty, tags[0], exifData[tags[0]])); } } if (this.ReadyToRefresh()) { percentDone = Convert.ToInt32(imageIndex / totalImages * 100.0); this.Progress.Report(new ProgressBarArguments(percentDone, String.Format("{0}/{1} images. Processing {2}", imageIndex, totalImages, image.File), true, false)); Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime); // Allows the UI thread to update every now and then } if (metadata.ContainsKey(this.metadataFieldName) == false) { if (this.clearIfNoMetadata) { // Clear the data field if there is no metadata... if (dataLabelToUpdate == Constant.DatabaseColumn.DateTime) { image.SetDateTimeOffsetFromFileInfo(this.fileDatabase.FolderPath); imagesToUpdate.Add(image.GetDateTimeColumnTuples()); keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - date/time reread from file")); } else { List <ColumnTuple> clearField = new List <ColumnTuple>() { new ColumnTuple(this.dataLabelByLabel[this.dataFieldLabel], String.Empty) }; imagesToUpdate.Add(new ColumnTuplesWithWhere(clearField, image.ID)); keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - data field is cleared")); } } else { keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - data field remains unaltered")); } continue; } string metadataValue = metadata[this.metadataFieldName].Value; ColumnTuplesWithWhere imageUpdate; if (dataLabelToUpdate == Constant.DatabaseColumn.DateTime) { if (DateTimeHandler.TryParseMetadataDateTaken(metadataValue, imageSetTimeZone, out DateTimeOffset metadataDateTime)) { image.SetDateTimeOffset(metadataDateTime); imageUpdate = image.GetDateTimeColumnTuples(); keyValueList.Add(new KeyValuePair <string, string>(image.File, metadataValue)); } else { keyValueList.Add(new KeyValuePair <string, string>(image.File, String.Format("'{0}' - data field remains unaltered - not a valid date/time.", metadataValue))); continue; } } else { imageUpdate = new ColumnTuplesWithWhere(new List <ColumnTuple>() { new ColumnTuple(dataLabelToUpdate, metadataValue) }, image.ID); keyValueList.Add(new KeyValuePair <string, string>(image.File, metadataValue)); } imagesToUpdate.Add(imageUpdate); } this.IsAnyDataUpdated = true; this.Progress.Report(new ProgressBarArguments(100, String.Format("Writing metadata for {0} files. Please wait...", totalImages), false, true)); Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime); // Allows the UI thread to update every now and then this.fileDatabase.UpdateFiles(imagesToUpdate); return keyValueList; }, this.Token).ConfigureAwait(true)); }
public ExchangePlan(ImageRow imageRow) : base(imageRow) { }
public Role(ImageRow imageRow) : base(imageRow) { }
public CommonModule(ImageRow imageRow) : base(imageRow) { }
public Enum(ImageRow row) : base(row) { }
public static ImageColumn <T> Column <T>(this ImageRow <T> row, int col) where T : struct, IEquatable <T> { return(row.Image.Column(col)); }
public Versions(ImageRow row) : base(row) { }
public static ImageColumn <T>[] Columns <T>(this ImageRow <T> row) where T : struct, IEquatable <T> { return(row.Image.Columns()); }
public ImageRowChangeEvent(ImageRow row, global::System.Data.DataRowAction action) { this.eventRow = row; this.eventAction = action; }
public void Show(bool isVisible, int maxNumberImagesToDisplay) { ImageRow currentImageRow = timelapseWindow?.DataHandler?.ImageCache?.Current; TimeSpan timeThreshold = timelapseWindow.State.EpisodeTimeThreshold; if (isVisible == false || currentImageRow == null || FileDatabase == null) { // Hide the popup if asked or if basic data isn't available, including deleting the children this.IsOpen = false; this.Child = null; return; } // Images or placeholders will be contained in a horizontal stack panel, which in turn is the popup's child StackPanel sp = new StackPanel { Orientation = Orientation.Horizontal }; this.Child = sp; double width = 0; // Used to calculate the placement offset of the popup relative to the placement target double height; // Add a visual marker to show the position of the label in the image list Label label = EpisodePopup.CreateLabel("^", this.ImageHeight); label.VerticalAlignment = VerticalAlignment.Top; width += label.Width; height = label.Height; sp.Children.Add(label); int margin = 2; FileTable fileTable; // To hold the results of the database selection as a table of ImageRows // We will only consider images whose relative path is the same as the current file string relativePath = currentImageRow.RelativePath; // Calculate the lower and upper extent of the range of dates we should examine // The maximum date range we need to consider would be the current date plus/minus the (time threshold * the number of images we could display), // While this could produce more hits than we need, it should give us a relatively short table of possible candidates DateTime lowerDateTime = currentImageRow.DateTime - TimeSpan.FromTicks(timeThreshold.Ticks * maxNumberImagesToDisplay); DateTime upperDateTime = currentImageRow.DateTime + TimeSpan.FromTicks(timeThreshold.Ticks * maxNumberImagesToDisplay); string slowerDateTime = DateTimeHandler.ToStringDatabaseDateTime(lowerDateTime); string supperDateTime = DateTimeHandler.ToStringDatabaseDateTime(upperDateTime); // Get a table of files (sorted by datetime) with that relative path which falls between the lower and upper date range DataTable dt = this.FileDatabase.GetIDandDateWithRelativePathAndBetweenDates(relativePath, slowerDateTime, supperDateTime); // Find the current image in that table by its ID int rowWithCurrentImageRowID = -1; int availableRows = dt.Rows.Count; for (int i = 0; i < availableRows; i++) { if (Convert.ToInt64(dt.Rows[i][0]) == currentImageRow.ID) { rowWithCurrentImageRowID = i; break; } } // From that current image, alternate between going to the previous/next row. // If the date difference between alternating successive images is less than the time threshold, // display it. int goBackwardsRow = rowWithCurrentImageRowID - 1; int goForwardsRow = rowWithCurrentImageRowID + 1; int imagesLeftToDisplay = maxNumberImagesToDisplay; DateTime lastBackwardsDateTime = currentImageRow.DateTime; DateTime lastForwardsDateTime = currentImageRow.DateTime; while (true && (goBackwardsRow >= 0 || goForwardsRow < availableRows)) { // Abort when there is no more work to do if (imagesLeftToDisplay <= 0) { break; } // Start on the left if (goBackwardsRow >= 0) { // Add a popup image to the left of the caret using (fileTable = this.FileDatabase.SelectFileInDataTableById(dt.Rows[goBackwardsRow][0].ToString())) { if (fileTable.Any()) { if ((lastBackwardsDateTime - fileTable[0].DateTime).Duration() <= timeThreshold) { Image image = EpisodePopup.CreateImage(fileTable[0], margin, this.ImageHeight); width += image.Source.Width; height = Math.Max(height, image.Source.Height); // Create a canvas containing the image and bounding boxes (if detections are on) sp.Children.Insert(0, CreateCanvasWithBoundingBoxesAndImage(image, height, margin, fileTable[0].ID)); imagesLeftToDisplay--; lastBackwardsDateTime = fileTable[0].DateTime; } else { // Stop searching backwards goBackwardsRow = -1; } } } goBackwardsRow--; } // Now try to add a popup image to the right if we still have some more images left to display if (goForwardsRow < availableRows && imagesLeftToDisplay > 0) { using (fileTable = this.FileDatabase.SelectFileInDataTableById(dt.Rows[goForwardsRow][0].ToString())) { if (fileTable.Any()) { if ((lastForwardsDateTime - fileTable[0].DateTime).Duration() <= timeThreshold) { Image image = EpisodePopup.CreateImage(fileTable[0], margin, this.ImageHeight); width += image.Source.Width; height = Math.Max(height, image.Source.Height); // Create a canvas containing the image and bounding box sp.Children.Add(CreateCanvasWithBoundingBoxesAndImage(image, height, margin, fileTable[0].ID)); imagesLeftToDisplay--; lastBackwardsDateTime = fileTable[0].DateTime; } else { // Stop searching forwards goForwardsRow = availableRows; } } } goForwardsRow++; } } label.Height = Math.Max(label.Height, height); // So it extends to the top of the popup // Position and open the popup so it appears horizontallhy centered just above the cursor this.HorizontalOffset = this.markableCanvas.ActualWidth / 2.0 - width / 2.0; this.VerticalOffset = -height - 2 * margin; this.IsOpen = true; // Cleanup dt.Dispose(); }
public SharedAttribute(ImageRow imageRow) : base(imageRow) { }
public Configuration(ImageRow row) : base(row) { }
public Catalog(ImageRow imageRow) : base(imageRow) { }
public Subsystem(ImageRow imageRow) : base(imageRow) { }
// Returns: // - the list of files whose dates have changed // - a collection of feedback information for each file whose dates were changed, each row detailing the file name and how the dates were changed // - the number of missing Files, if any private List <ImageRow> GetImageRowsWithChangedDates(IProgress <ProgressBarArguments> progress, int count, TimeZoneInfo imageSetTimeZone, ObservableCollection <DateTimeFeedbackTuple> feedbackRows, out int missingFiles) { List <ImageRow> filesToAdjust = new List <ImageRow>(); missingFiles = 0; for (int fileIndex = 0; fileIndex < count; ++fileIndex) { if (Token.IsCancellationRequested) { // A cancel was requested. Clear all pending changes and abort feedbackRows.Clear(); break; } // We will store the various times here ImageRow file = this.fileDatabase.FileTable[fileIndex]; DateTimeOffset originalDateTime = file.DateTimeIncorporatingOffset; string feedbackMessage = string.Empty; try { // Get the image (if its there), get the new dates/times, and add it to the list of images to be updated // Note that if the image can't be created, we will just to the catch. bool usingMetadataTimestamp = true; if (file.FileExists(this.fileDatabase.FolderPath) == false) { // The file does not exist. Generate a feedback message missingFiles++; } else { // Read the date from the file, and check to see if its different from the recorded date DateTimeAdjustmentEnum dateTimeAdjustment = file.TryReadDateTimeOriginalFromMetadata(this.fileDatabase.FolderPath, imageSetTimeZone); if (dateTimeAdjustment == DateTimeAdjustmentEnum.MetadataNotUsed) { // We couldn't read the metadata, so get a candidate date/time from the file info instead file.SetDateTimeOffsetFromFileInfo(this.fileDatabase.FolderPath); usingMetadataTimestamp = false; } DateTimeOffset rescannedDateTime = file.DateTimeIncorporatingOffset; bool sameDate = rescannedDateTime.Date == originalDateTime.Date; bool sameTime = rescannedDateTime.TimeOfDay == originalDateTime.TimeOfDay; bool sameUTCOffset = rescannedDateTime.Offset == originalDateTime.Offset; if (!(sameDate && sameTime && sameUTCOffset)) { // Date has been updated - add it to the queue of files to be processed, and generate a feedback message. filesToAdjust.Add(file); feedbackMessage = "\x2713"; // Checkmark feedbackMessage += DateTimeHandler.ToStringDisplayDateTime(originalDateTime) + " \x2192 " + DateTimeHandler.ToStringDisplayDateTime(rescannedDateTime); feedbackMessage += usingMetadataTimestamp ? " (read from metadata)" : " (read from file)"; feedbackRows.Add(new DateTimeFeedbackTuple(file.File, feedbackMessage)); } } } catch (Exception exception) { // This shouldn't happen, but just in case. TracePrint.PrintMessage(string.Format("Unexpected exception processing '{0}' in DateTimeReread. {1}", file.File, exception.ToString())); feedbackMessage += string.Format("\x2716 skipping: {0}", exception.Message); feedbackRows.Add(new DateTimeFeedbackTuple(file.File, feedbackMessage)); break; } // Update the progress bar every time interval to indicate what file we are working on TimeSpan intervalFromLastRefresh = DateTime.Now - this.lastRefreshDateTime; if (intervalFromLastRefresh > Constant.ThrottleValues.ProgressBarRefreshInterval) { int percentDone = Convert.ToInt32(fileIndex / Convert.ToDouble(count) * 100.0); progress.Report(new ProgressBarArguments(percentDone, String.Format("Pass 1: Checking dates for {0} / {1} files", fileIndex, count), true, false)); Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime); this.lastRefreshDateTime = DateTime.Now; } } return(filesToAdjust); }
public FunctionalOption(ImageRow imageRow) : base(imageRow) { }
public DocumentJournal(ImageRow imageRow) : base(imageRow) { }
// Menu selections for propagating or copying the current value of this control to all images // Copy the last non-empty value in this control preceding this file up to the current image protected virtual void MenuItemPropagateFromLastValue_Click(object sender, RoutedEventArgs e) { // Check the arguments for null ThrowIf.IsNullArgument(sender, nameof(sender)); // Get the chosen data entry control DataEntryControl control = (DataEntryControl)((MenuItem)sender).Tag; if (control == null) { return; } bool checkForZero = control is DataEntryCounter; bool isFlag = control is DataEntryFlag; int indexToCopyFrom = -1; ImageRow valueSource = null; string valueToCopy = checkForZero ? "0" : String.Empty; // Search for the row with some value in it, starting from the previous row int currentRowIndex = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.CurrentRow : this.ThumbnailGrid.GetSelected()[0]; for (int previousIndex = currentRowIndex - 1; previousIndex >= 0; previousIndex--) { ImageRow file = this.FileDatabase.FileTable[previousIndex]; if (file == null) { continue; } valueToCopy = file.GetValueDatabaseString(control.DataLabel); if (valueToCopy == null) { continue; } valueToCopy = valueToCopy.Trim(); if (valueToCopy.Length > 0) { if ((checkForZero && !valueToCopy.Equals("0")) || // Skip over non-zero values for counters (isFlag && !valueToCopy.Equals(Constant.BooleanValue.False, StringComparison.OrdinalIgnoreCase)) || // Skip over false values for flags (!checkForZero && !isFlag)) { indexToCopyFrom = previousIndex; // We found a non-empty value valueSource = file; break; } } } string newContent = valueToCopy; if (indexToCopyFrom < 0) { // Display a dialog box saying there is nothing to propagate. // Note that this should never be displayed, as the menu shouldn't be highlit if there is nothing to propagate // But just in case... Dialogs.DataEntryNothingToPropagateDialog(Application.Current.MainWindow); return; } // Display the appropriate dialog box that explains what will happen. Arguments indicate what is to be propagated and how many files will be affected int filesAffected = currentRowIndex - indexToCopyFrom; if (Dialogs.DataEntryConfirmPropagateFromLastValueDialog(Application.Current.MainWindow, valueToCopy, filesAffected) != true) { return; // operation cancelled // newContent = this.FileDatabase.FileTable[currentRowIndex].GetValueDisplayString(control.DataLabel); // No change, so return the current value } // Update the affected files. Note that we start on the row after the one with a value in it to the current row. Mouse.OverrideCursor = Cursors.Wait; this.FileDatabase.UpdateFiles(valueSource, control.DataLabel, indexToCopyFrom + 1, currentRowIndex); control.SetContentAndTooltip(newContent); Mouse.OverrideCursor = null; }
public CommonForm(ImageRow imageRow) : base(imageRow) { }
// Add a row to the tuple, which in turn will update the grid. public void AddFeedbackRow(string sampleFileName, string currentDate, string newDate, string numFilesWithThatDate, ImageRow imageRow, AmbiguousDateRange ambiguousDateRange) { FeedbackRowTuple row = new FeedbackRowTuple(sampleFileName, currentDate, newDate, numFilesWithThatDate, imageRow, ambiguousDateRange); this.feedbackRows.Add(row); }
public Sequence(ImageRow imageRow) : base(imageRow) { }