public static bool TryFindFocusedControl(IInputElement focusedElement, out DataEntryControl focusedControl)
        {
            if (focusedElement is FrameworkElement focusedFrameworkElement)
            {
                focusedControl = (DataEntryControl)focusedFrameworkElement.Tag;
                if (focusedControl != null)
                {
                    return(true);
                }

                // for complex controls which dynamic generate child controls, such as date time pickers, the tag of the focused element can't be set
                // so try to locate a parent of the focused element with a tag indicating the control
                FrameworkElement parent = null;
                if (focusedFrameworkElement.Parent != null && focusedFrameworkElement.TemplatedParent is FrameworkElement)
                {
                    parent = (FrameworkElement)focusedFrameworkElement.Parent;
                }
                else if (focusedFrameworkElement.TemplatedParent != null && focusedFrameworkElement.TemplatedParent is FrameworkElement element)
                {
                    parent = element;
                }

                if (parent != null)
                {
                    return(DataEntryHandler.TryFindFocusedControl(parent, out focusedControl));
                }
            }
            focusedControl = null;
            return(false);
        }
        // Paste the contents of the clipboard into the current or selected controls
        // Note that we don't do any checks against the control's type, as that would be handled by the menu enablement
        protected virtual void MenuItemPasteFromClipboard_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;
            }
            string newContent = Clipboard.GetText().Trim();

            if (control is DataEntryCounter _)
            {
                // For counters, removing any leading 0's, but if this ends up with an empty string, then revert to 0
                newContent = newContent.TrimStart(new Char[] { '0' });
                if (string.IsNullOrEmpty(newContent))
                {
                    newContent = "0";
                }
            }
            control.SetContentAndTooltip(newContent);
            this.UpdateRowsDependingOnThumbnailGridState(control.DataLabel, newContent);
        }
        // Copy the current value of this control to all images
        protected virtual void MenuItemCopyCurrentValueToAll_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;
            }

            // Display a dialog box that explains what will happen. Arguments indicate how many files will be affected, and is tuned to the type of control
            bool checkForZero  = control is DataEntryCounter;
            int  filesAffected = this.FileDatabase.CountAllCurrentlySelectedFiles;

            if (Dialogs.DataEntryConfirmCopyCurrentValueToAllDialog(Application.Current.MainWindow, control.Content, filesAffected, checkForZero) != true)
            {
                return;
            }

            // Update all files to match the value of the control (identified by the data label) in the currently selected image row.
            Mouse.OverrideCursor = Cursors.Wait;
            ImageRow imageRow = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.Current : this.FileDatabase.FileTable[this.ThumbnailGrid.GetSelected()[0]];

            this.FileDatabase.UpdateFiles(imageRow, control.DataLabel);
            Mouse.OverrideCursor = null;
        }
Example #4
0
        // Return true if there is a non-empty value available
        public bool IsCopyFromLastNonEmptyValuePossible(DataEntryControl control)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(control, nameof(control));

            bool checkCounter = control is DataEntryCounter;
            bool checkFlag    = control is DataEntryFlag;
            int  nearestRowWithCopyableValue = -1;

            for (int fileIndex = this.ImageCache.CurrentRow - 1; fileIndex >= 0; fileIndex--)
            {
                // Search for the row with some value in it, starting from the previous row
                string valueToCopy = this.FileDatabase.FileTable[fileIndex].GetValueDatabaseString(control.DataLabel);
                if (String.IsNullOrWhiteSpace(valueToCopy) == false)
                {
                    // for flags, we skip over falses
                    // for counters, we skip over 0
                    // for all others, any value will work as long as its not null or white space
                    if ((checkFlag && !valueToCopy.Equals("false")) ||
                        (checkCounter && !valueToCopy.Equals("0")) ||
                        (!checkCounter && !checkFlag))
                    {
                        nearestRowWithCopyableValue = fileIndex;    // We found a non-empty value
                        break;
                    }
                }
            }
            return(nearestRowWithCopyableValue >= 0);
        }
        // When a flag changes, update the particular flag field(s) in the database
        private void FlagControl_CheckedChanged(object sender, RoutedEventArgs e)
        {
            if (this.IsProgrammaticControlUpdate)
            {
                return;
            }
            CheckBox         checkBox = (CheckBox)sender;
            DataEntryControl control  = (DataEntryControl)checkBox.Tag;
            string           value    = ((bool)checkBox.IsChecked) ? Constant.BooleanValue.True : Constant.BooleanValue.False;

            control.SetContentAndTooltip(value);
            this.UpdateRowsDependingOnThumbnailGridState(control.DataLabel, control.Content);
        }
        // When the number in a particular counter box changes, update the particular counter field(s) in the database
        private void CounterControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs <object> e)
        {
            if (this.IsProgrammaticControlUpdate)
            {
                return;
            }
            IntegerUpDown integerUpDown = (IntegerUpDown)sender;

            // Get the key identifying the control, and then add its value to the database
            DataEntryControl control = (DataEntryControl)integerUpDown.Tag;

            control.SetContentAndTooltip(integerUpDown.Value.ToString());
            this.UpdateRowsDependingOnThumbnailGridState(control.DataLabel, control.Content);
        }
Example #7
0
        // Copy the  value of the current control to the clipboard
        protected virtual void MenuItemCopyToClipboard_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;
            }
            Clipboard.SetText(control.Content);
        }
        // Return true if there is a non-empty value available
        public bool IsCopyFromLastNonEmptyValuePossible(DataEntryControl control)
        {
            int currentIndex = 0; // So we can print the value in the catch

            // Check the arguments for null
            ThrowIf.IsNullArgument(control, nameof(control));

            bool checkCounter = control is DataEntryCounter;
            bool checkFlag    = control is DataEntryFlag;
            int  nearestRowWithCopyableValue = -1;

            // Its in a try/catch as very very occassionally we get a 'system.indexoutofrangeexception'
            try
            {
                // The current row depends on wheter we are in the thumbnail grid or the normal view
                int currentRow = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.CurrentRow : this.ThumbnailGrid.GetSelected()[0];
                for (int fileIndex = currentRow - 1; fileIndex >= 0; fileIndex--)
                {
                    currentIndex = fileIndex;
                    // Search for the row with some value in it, starting from the previous row
                    string valueToCopy = this.FileDatabase.FileTable[fileIndex].GetValueDatabaseString(control.DataLabel);
                    if (String.IsNullOrWhiteSpace(valueToCopy) == false)
                    {
                        // for flags, we skip over falses
                        // for counters, we skip over 0
                        // for all others, any value will work as long as its not null or white space
                        if ((checkFlag && !valueToCopy.Equals("false")) ||
                            (checkCounter && !valueToCopy.Equals("0")) ||
                            (!checkCounter && !checkFlag))
                        {
                            nearestRowWithCopyableValue = fileIndex;    // We found a non-empty value
                            break;
                        }
                    }
                }
            }
            catch (IndexOutOfRangeException e)
            {
                // I don't know why we get this occassional error, so this is an attempt to print out the result so we can debug it
                System.Diagnostics.Debug.Print(String.Format("IsCopyFromLastNonEmptyValuePossible: IndexOutOfRange Exception, where index is: {0}{1}{2}", currentIndex, Environment.NewLine, e.Message));
                return(nearestRowWithCopyableValue >= 0);
            }
            return(nearestRowWithCopyableValue >= 0);
        }
        // Propagate the current value of this control forward from this point across the current set of selected images
        protected virtual void MenuItemPropagateForward_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;
            }

            int currentRowIndex = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.CurrentRow : this.ThumbnailGrid.GetSelected()[0];
            int imagesAffected  = this.FileDatabase.CountAllCurrentlySelectedFiles - currentRowIndex - 1;

            if (imagesAffected == 0)
            {
                // Display a dialog box saying there is nothing to copy forward.
                // Note that this should never be displayed, as the menu shouldn't be highlit if we are on the last image
                // But just in case...
                Dialogs.DataEntryNothingToCopyForwardDialog(Application.Current.MainWindow);
                return;
            }

            // Display the appropriate dialog box that explains what will happen. Arguments indicate how many files will be affected, and is tuned to the type of control
            ImageRow imageRow     = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.Current : this.FileDatabase.FileTable[this.ThumbnailGrid.GetSelected()[0]];
            string   valueToCopy  = imageRow.GetValueDisplayString(control.DataLabel);
            bool     checkForZero = control is DataEntryCounter;

            if (Dialogs.DataEntryConfirmCopyForwardDialog(Application.Current.MainWindow, valueToCopy, imagesAffected, checkForZero) != true)
            {
                return;
            }

            // Update the files from the next row (as we are copying from the current row) to the end.
            Mouse.OverrideCursor = Cursors.Wait;
            int nextRowIndex = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.CurrentRow + 1 : this.ThumbnailGrid.GetSelected()[0] + 1;

            this.FileDatabase.UpdateFiles(imageRow, control.DataLabel, nextRowIndex, this.FileDatabase.CountAllCurrentlySelectedFiles - 1);
            Mouse.OverrideCursor = null;
        }
        // When a choice changes, update the particular choice field(s) in the database
        private void ChoiceControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.IsProgrammaticControlUpdate)
            {
                return;
            }
            ComboBox comboBox = (ComboBox)sender;

            if (comboBox.SelectedItem == null)
            {
                // no item selected (probably the user cancelled)
                return;
            }

            // Get the key identifying the control, and then add its value to the database
            DataEntryControl control = (DataEntryControl)comboBox.Tag;

            control.SetContentAndTooltip(((ComboBoxItem)comboBox.SelectedItem).Content.ToString());
            this.UpdateRowsDependingOnThumbnailGridState(control.DataLabel, control.Content);
        }
        // Copy the  value of the current control to the clipboard
        protected virtual void MenuItemCopyToClipboard_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;
            }

            // Its in a try / catch as one user reported an unusual error: OpenClipboardFailed
            try
            {
                Clipboard.SetText(control.Content);
            }
            catch
            {
                System.Diagnostics.Debug.Print("Error in setting text in clipboard (see MenuItemCopyToClipboard_Click in DataEntryHandler");
            }
        }
        // Enable or disable particular context menu items
        protected virtual void Container_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(sender, nameof(sender));

            StackPanel       stackPanel = (StackPanel)sender;
            DataEntryControl control    = (DataEntryControl)stackPanel.Tag;

            MenuItem menuItemCopyToAll              = (MenuItem)stackPanel.ContextMenu.Items[DataEntryHandler.CopyToAllIndex];
            MenuItem menuItemCopyForward            = (MenuItem)stackPanel.ContextMenu.Items[DataEntryHandler.CopyForwardIndex];
            MenuItem menuItemPropagateFromLastValue = (MenuItem)stackPanel.ContextMenu.Items[DataEntryHandler.PropagateFromLastValueIndex];
            MenuItem menuItemCopyToClipboard        = (MenuItem)stackPanel.ContextMenu.Items[DataEntryHandler.CopyToClipboardIndex];
            MenuItem menuItemPasteFromClipboard     = (MenuItem)stackPanel.ContextMenu.Items[DataEntryHandler.PasteFromClipboardIndex];

            // Behaviour:
            // - if the ThumbnailInCell is visible, disable Copy to all / Copy forward / Propagate if a single item isn't selected
            // - otherwise enable the menu item only if the resulting action is coherent
            bool enabledIsPossible = this.ThumbnailGrid.IsVisible == false || this.ThumbnailGrid.SelectedCount() == 1;

            menuItemCopyToAll.IsEnabled              = enabledIsPossible;
            menuItemCopyForward.IsEnabled            = enabledIsPossible && (menuItemCopyForward.IsEnabled = this.IsCopyForwardPossible());
            menuItemPropagateFromLastValue.IsEnabled = enabledIsPossible && this.IsCopyFromLastNonEmptyValuePossible(control);

            // Enable Copy menu if
            // - its not empty / white space and not in the overview with different contents (i.e., ellipsis is showing)
            menuItemCopyToClipboard.IsEnabled = !(String.IsNullOrWhiteSpace(control.Content) || control.Content == Constant.Unicode.Ellipsis);

            // Enable Paste menu only if
            // - the clipboard is not empty or white space,
            // - the string matches the contents expected by the control's type
            // - we are not in the overview with different contents selected (i.e., ellipsis is showing)
            // Its in a try / catch as one user reported an unusual error: OpenClipboardFailed
            string clipboardText;

            try
            {
                clipboardText = Clipboard.GetText().Trim();
            }
            catch
            {
                clipboardText = String.Empty;
                System.Diagnostics.Debug.Print("Error in setting text in clipboard (see Container_PreviewMouseRightButtonDown in DataEntryHandler");
            }
            if (String.IsNullOrEmpty(clipboardText))
            {
                menuItemPasteFromClipboard.IsEnabled = false;
            }
            else
            {
                if (control is DataEntryNote _)
                {
                    // Any string is valid
                    menuItemPasteFromClipboard.IsEnabled = true;
                }
                else if (control is DataEntryFlag _)
                {
                    // Only true / false is valid
                    menuItemPasteFromClipboard.IsEnabled = (clipboardText == "true" || clipboardText == "false");
                }
                else if (control is DataEntryCounter _)
                {
                    // Only a positive integer is valid
                    menuItemPasteFromClipboard.IsEnabled = Int32.TryParse(clipboardText, out int x) && x >= 0;
                }
                else if (control is DataEntryChoice choiceControl)
                {
                    // Only a value present as a menu choice is valid
                    menuItemPasteFromClipboard.IsEnabled = false;
                    ComboBox comboBox = choiceControl.ContentControl;
                    foreach (ComboBoxItem cbi in comboBox.Items)
                    {
                        if (clipboardText == ((string)cbi.Content).Trim())
                        {
                            // We found a matching value, so pasting is possible
                            menuItemPasteFromClipboard.IsEnabled = true;
                            break;
                        }
                    }
                }

                // Alter the paste header to show the text that will be pasted e.g Paste 'Lion'
                if (menuItemPasteFromClipboard.IsEnabled)
                {
                    if (control is DataEntryCounter _)
                    {
                        // removing any leading 0's, but if its empty make it a 0
                        clipboardText = clipboardText.TrimStart(new Char[] { '0' });
                        if (string.IsNullOrEmpty(clipboardText))
                        {
                            clipboardText = "0";
                        }
                    }
                    menuItemPasteFromClipboard.Header = "Paste '" + (clipboardText.Length > 20 ? clipboardText.Substring(0, 20) + Constant.Unicode.Ellipsis : clipboardText) + "'";
                }
                else
                {
                    // Since there is nothing in the clipboard, just show 'Paste'
                    menuItemPasteFromClipboard.Header = "Paste";
                }

                // Alter the copy header to show the text that will be copied, i.e. Copy 'Lion'
                if (menuItemCopyToClipboard.IsEnabled)
                {
                    string content = control.Content.Trim();
                    menuItemCopyToClipboard.Header = "Copy '" + (content.Length > 20 ? content.Substring(0, 20) + Constant.Unicode.Ellipsis : content) + "'";
                }
                else
                {
                    // Since there an empty string to Copy, just show 'Copy'
                    menuItemCopyToClipboard.Header = "Copy";
                }
            }
        }
        // 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;
        }
        // Create the Context menu, incluidng settings its callbakcs
        private void SetContextMenuCallbacks(DataEntryControl control)
        {
            if (Util.GlobalReferences.TimelapseState.IsViewOnly)
            {
                return;
            }
            // Start with an empty clipboard
            // Its in a try / catch as one user reported an unusual error: OpenClipboardFailed
            try
            {
                Clipboard.SetText(String.Empty);
            }
            catch
            {
                System.Diagnostics.Debug.Print("Error in setting text in clipboard (see SetContextMenuCallbacks in DataEntryHandler");
            }

            MenuItem menuItemPropagateFromLastValue = new MenuItem()
            {
                IsCheckable = false,
                Tag         = control,
                Header      = "Propagate from the last non-empty value to here"
            };

            if (control is DataEntryCounter)
            {
                menuItemPropagateFromLastValue.Header = "Propagate from the last non-zero value to here";
            }
            menuItemPropagateFromLastValue.Click += this.MenuItemPropagateFromLastValue_Click;

            MenuItem menuItemCopyForward = new MenuItem()
            {
                IsCheckable = false,
                Header      = "Copy forward to end",
                ToolTip     = "The value of this field will be copied forward from this file to the last file in this set",
                Tag         = control
            };

            menuItemCopyForward.Click += this.MenuItemPropagateForward_Click;
            MenuItem menuItemCopyCurrentValue = new MenuItem()
            {
                IsCheckable = false,
                Header      = "Copy to all",
                Tag         = control
            };

            menuItemCopyCurrentValue.Click += this.MenuItemCopyCurrentValueToAll_Click;

            MenuItem menuItemCopy = new MenuItem()
            {
                IsCheckable = false,
                Header      = "Copy",
                ToolTip     = "Copy will copy this field's entire content to the clipboard",
                Tag         = control
            };

            menuItemCopy.Click += this.MenuItemCopyToClipboard_Click;

            MenuItem menuItemPaste = new MenuItem()
            {
                IsCheckable = false,
                Header      = "Paste",
                ToolTip     = "Paste will replace this field's content with the clipboard's content",
                Tag         = control
            };

            menuItemPaste.Click += this.MenuItemPasteFromClipboard_Click;

            // DataEntrHandler.PropagateFromLastValueIndex and CopyForwardIndex must be kept in sync with the add order here
            ContextMenu menu = new ContextMenu();

            menu.Items.Add(menuItemPropagateFromLastValue);
            menu.Items.Add(menuItemCopyForward);
            menu.Items.Add(menuItemCopyCurrentValue);
            Separator menuSeparator = new Separator();

            menu.Items.Add(menuSeparator);
            menu.Items.Add(menuItemCopy);
            menu.Items.Add(menuItemPaste);

            control.Container.ContextMenu = menu;
            control.Container.PreviewMouseRightButtonDown += this.Container_PreviewMouseRightButtonDown;

            // For the File/Folder/RelativePath controls, all which are read only, hide the irrelevant menu items.
            // This could be made more efficient by simply not creating those items, but given the low case we just left it as is.
            if (control.DataLabel == Constant.DatabaseColumn.File || control.DataLabel == Constant.DatabaseColumn.Folder || control.DataLabel == Constant.DatabaseColumn.RelativePath)
            {
                if (control is DataEntryNote note)
                {
                    note.ContentControl.ContextMenu           = menu;
                    menuItemPropagateFromLastValue.Visibility = Visibility.Collapsed;
                    menuItemCopyForward.Visibility            = Visibility.Collapsed;
                    menuItemCopyCurrentValue.Visibility       = Visibility.Collapsed;
                    menuSeparator.Visibility = Visibility.Collapsed;
                    if (control.ContentReadOnly)
                    {
                        menuItemPaste.Visibility = Visibility.Collapsed;
                    }
                }
            }
            else if (control is DataEntryCounter counter)
            {
                counter.ContentControl.ContextMenu = menu;
            }
            else if (control is DataEntryNote note)
            {
                note.ContentControl.ContextMenu = menu;
            }
            else if (control is DataEntryChoice choice)
            {
                choice.ContentControl.ContextMenu = menu;
            }
            else if (control is DataEntryFlag flag)
            {
                flag.ContentControl.ContextMenu = menu;
            }
            else
            {
                throw new NotSupportedException(String.Format("Unhandled control type {0}.", control.GetType().Name));
            }
        }