/// Copyright (c) 2021 Iiro Iivanainen, Harri Linna, Jere Pakkanen, Riikka Vilavaara /// /// Permission is hereby granted, free of charge, to any person obtaining /// a copy of this software and associated documentation files (the /// "Software"), to deal in the Software without restriction, including /// without limitation the rights to use, copy, modify, merge, publish, /// distribute, sublicense, and/or sell copies of the Software, and to /// permit persons to whom the Software is furnished to do so, subject to /// the following conditions: /// /// The above copyright notice and this permission notice shall be included /// in all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, /// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF /// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. /// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY /// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, /// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE /// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /// using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Shapes; using AvalonDock.Layout; using GroundhogApp.Dialogs; using Microsoft.Win32; using CoreLibrary; using System.IO; using System.Reflection; namespace GroundhogApp { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { private readonly MainWindowViewModel ViewModel = new(); /// /// Constructor for MainWindow. /// public MainWindow() { InitializeComponent(); DataContext = ViewModel; SetListViewFilter(); //TempDocu is a temporary LayoutDocument element, that //is closed at the start to get rid of empty Attached Data //-section when opening the program. TempDocu.Close(); } /// /// For setting and resetting the listview and its filter. /// private void SetListViewFilter() { ConnectorsListView.ItemsSource = ViewModel.AttData; CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(ConnectorsListView.ItemsSource); view.Filter = ViewModel.CollectionFilter; } /// /// Event handler for TextChanged event in txtFiltter. /// /// Event sender. /// Event arguments. private void TxtFilter_TextChanged(object sender, TextChangedEventArgs e) { var c = sender as Control; if (!c.IsLoaded) return; CollectionViewSource.GetDefaultView(ConnectorsListView.ItemsSource).Refresh(); } /// /// Attempts to open the manual html-file. /// /// /// private void OpenHelp(object sender, RoutedEventArgs e) { if (!Utilities.OpenFile("Manual\\Manual.html")) { MessageBox.Show("Could not find the manual"); return; } } /// /// Opens the About dialog displaying information regarding the application. /// /// /// private void OpenAbout(object sender, RoutedEventArgs e) { MessageBox.Show( "GeoLab version 1.0.0\n" + "\n" + "Authors:\n" + "Iiro Iivanainen, Harri Linna, Jere Pakkanen, Riikka Vilavaara\n" + "\n" + "Licensed under the MIT License\n" + "Copyright (c) 2021 Geological Survey of Finland GTK\n" + "\n" + "Permission is hereby granted, free of charge, to any person obtaining a copy " + "of this software and associated documentation files (the \"Software\"), to deal " + "in the Software without restriction, including without limitation the rights " + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell " + "copies of the Software, and to permit persons to whom the Software is " + "furnished to do so, subject to the following conditions:\n" + "\n" + "The above copyright notice and this permission notice shall be included in " + "all copies or substantial portions of the Software.\n" + "\n" + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR " + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, " + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENTT SHALL " + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER " + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING " + "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " + "IN THE SOFTWARE." , "About", MessageBoxButton.OK); } /// /// OpenFile event handler for the list of connectors. /// /// Event sender. /// Event arguments. private void OpenFile(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; var filePath = (RelativeFilepath)connector.GetType().GetProperty("Filepath").GetValue(connector, null); if (!Utilities.OpenFile(filePath.GetPath())) { MessageBox.Show("File does not exists in the specified location " + filePath, "Alert", MessageBoxButton.OK, MessageBoxImage.Information); return; } } /// /// Open Folder event handler. /// /// Event sender. /// Event arguments. private void OpenFolder(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; var filePath = (RelativeFilepath)connector.GetType().GetProperty("Filepath").GetValue(connector, null); if (Utilities.OpenFolderFileSelected(filePath.GetPath())) return; if (Utilities.OpenFolder(filePath.GetPath())) return; else MessageBox.Show("Could not find the folder of " + filePath, "Alert", MessageBoxButton.OK, MessageBoxImage.Information); } /// /// Turning on the Edit Mode on the application. /// /// Event sender /// Event arguments private void EditMode_Checked(object sender, RoutedEventArgs e) { editStatusBar.Visibility = Visibility.Visible; } /// /// Turning off Edit Mode on the application. /// /// Event sender /// Event arguments private void EditMode_Unchecked(object sender, RoutedEventArgs e) { editStatusBar.Visibility = Visibility.Hidden; } /// /// Adding connector to Map. /// /// /// private void AddConnector(object sender, RoutedEventArgs e) { // If chosen from menu the passed coordinates are the // "slice depths" by default. OpenAddConnectorDialog(ViewModel.ZYDepth, ViewModel.XZDepth, ViewModel.XYDepth); } /// /// Adjusting MapGrid rows and columns depending on how many MapControls /// are currently visible. /// /// Event sender. Not used. /// Event arguments. Not used. #pragma warning disable IDE0051 // Remove unused private members #pragma warning disable IDE0060 // Remove unused parameter private void SliceControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) #pragma warning restore IDE0060 // Remove unused parameter #pragma warning restore IDE0051 // Remove unused private members { int visibleChildren = 0; foreach (var child in MapGrid.Children) { // Next if statement just in case someone adds extra // elements to MapGrid. Shouldn't be the case. if (!(child is MapControl)) continue; if (((MapControl)child).Visibility == Visibility.Visible) visibleChildren++; } switch (visibleChildren) { case 1: MapGrid.Rows = 1; MapGrid.Columns = 1; break; case 2: MapGrid.Rows = 1; MapGrid.Columns = 2; break; case 3: MapGrid.Rows = 2; MapGrid.Columns = 2; break; default: break; } } GridViewColumnHeader _lastHeaderClicked = null; ListSortDirection _lastDirection = ListSortDirection.Ascending; /// /// For sorting the list displaying connectors. /// /// /// void ConnectorGridViewHeader_Click(object sender, RoutedEventArgs e) { ListSortDirection direction; if (e.OriginalSource is GridViewColumnHeader headerClicked) { if (headerClicked.Role != GridViewColumnHeaderRole.Padding) { if (headerClicked != _lastHeaderClicked) { direction = ListSortDirection.Ascending; } else { if (_lastDirection == ListSortDirection.Ascending) { direction = ListSortDirection.Descending; } else { direction = ListSortDirection.Ascending; } } var columnBinding = headerClicked.Column.DisplayMemberBinding as Binding; var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string; Sort(sortBy, direction); _lastHeaderClicked = headerClicked; _lastDirection = direction; } } } /// /// For sorting the listview. /// /// /// private void Sort(string sortBy, ListSortDirection direction) { ICollectionView dataView = CollectionViewSource.GetDefaultView(ConnectorsListView.ItemsSource); dataView.SortDescriptions.Clear(); SortDescription sd = new(sortBy, direction); dataView.SortDescriptions.Add(sd); dataView.Refresh(); } /// /// Opens a dialog for adding a new connector with given coordinates as /// initial values. /// /// /// /// private void OpenAddConnectorDialog(float xcoord, float ycoord, float zcoord) { var initialValues = new List> { // TODO: Allow more initial coordinates to be sent. new List { xcoord, ycoord, zcoord } }; var copyFolder = ViewModel.GetSampleSaveFolder(); var dlg = new ConnectorDialog(initialValues, copyFolder) { Owner = this }; bool? result = dlg.ShowDialog(); if (result == false) return; string filePath = dlg.FilePath; string connectorName = dlg.ConnectorName; string dataDescription = dlg.DataDescription; List> coordinates = dlg.Coordinates; bool copyToFolder = ConnectorDialog.CopyToFolder; try { ViewModel.AddAttData(filePath, connectorName, dataDescription, coordinates, copyToFolder); } catch (Exception e) when (e is IOException or UnauthorizedAccessException) { // Exception handling for possible file copying. MessageBox.Show(e.Message); } CollectionViewSource.GetDefaultView(ConnectorsListView.ItemsSource).Refresh(); } /// /// Selects coordinates for adding a new connector. /// /// Event sender /// Event arguments private void XYsliceControl_MapMouseDown(object sender, MouseButtonEventArgs e) { OpenAddConnectorDialog((float)XYsliceControl.Xpos, (float)XYsliceControl.Ypos, (float)ViewModel.XYDepth); } /// /// Selects coordinates for adding a new connector. /// /// Event sender /// Event arguments private void ZYsliceControl_MapMouseDown(object sender, MouseButtonEventArgs e) { OpenAddConnectorDialog((float)ViewModel.ZYDepth, (float)ZYsliceControl.Ypos, (float)ZYsliceControl.Xpos); } /// /// Selects coordinates for adding a new connector. /// /// Event sender /// Event arguments private void XZsliceControl_MapMouseDown(object sender, MouseButtonEventArgs e) { OpenAddConnectorDialog((float)XZsliceControl.Xpos, (float)ViewModel.XZDepth, (float)XZsliceControl.Ypos); } /// /// Connector doubleclicked from the Connectors ListView. /// /// Event sender /// Event arguments private void ConnectorsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) { var connector = ((ListViewItem)sender).Content; DisplayConnector(connector); } /// /// Selects a connector object from listview. /// /// /// private void ConnectorsListViewDisplay_Click(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; DisplayConnector(connector); } /// /// Switches the slices to display connector object from listview. /// /// /// private void ConnectorsListViewGoTo_Click(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; ViewModel.GoToConnector((AttachedData)connector); } /// /// Opens attached data as a tab. /// /// private void DisplayConnector(object connector) { string title = connector.ToString(); var filePath = (RelativeFilepath)connector.GetType().GetProperty("Filepath").GetValue(connector, null); var uc = new UserControls.DisplayAttachedData(filePath.GetPath()); AddToConnectedDataPane(uc, title); } /// /// Deletes the selected attached data. /// /// /// private void DeleteConnector(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; if (MessageBox.Show("Delete selected connector " + connector.ToString() + "? \n\r \n\r (The file will not be deleted)", "Delete Connector", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { ViewModel.DeleteAttData(connector); } CollectionViewSource.GetDefaultView(ConnectorsListView.ItemsSource).Refresh(); } /// /// Opens connector's metadata for editing. /// /// /// private void OpenMetaData(object sender, RoutedEventArgs e) { if (ConnectorsListView.SelectedIndex == -1) return; var connector = ConnectorsListView.SelectedItem; Metas metas = (Metas)connector.GetType().GetProperty("Metas").GetValue(connector, null); if (connector == null) return; MetaDataDialog dlg = new(metas); dlg.Title = connector.ToString() + " - Metadata"; dlg.Owner = this; bool? result = dlg.ShowDialog(); if (result == false) return; Metas editedMetas = dlg.metasEditable; ViewModel.ApplyMetadataChanges(metas, editedMetas); } /// /// Opens the sample metadata for editing. /// /// Event sender /// Event arguments private void ShowSampleMetaData(object sender, RoutedEventArgs e) { Metas metas = ViewModel.GetSampleMetaData(); MetaDataDialog dlg = new(metas); dlg.Title = "Sample Metadata"; dlg.Owner = this; bool? result = dlg.ShowDialog(); if (result == false) return; Metas editedMetas = dlg.metasEditable; ViewModel.ApplyMetadataChanges(metas, editedMetas); } /// /// Adds a LayoutDocument and makes it active. /// /// Content within the new DataPane private void AddToConnectedDataPane(object content, string title) { LayoutDocument dataPane = new(); dataPane.Content = content; dataPane.Title = title; if (ConnectedDataPaneGrp.ChildrenCount == 0) { MapAndConnectorPaneGrp.Children.Add(ConnectedDataPaneGrp); ConnectedDataPaneGrp.Children.Add(ConnectedDataPane); } ConnectedDataPane.Children.Add(dataPane); dataPane.IsActive = true; } /// /// Opening dialog for adding a Map and handling the /// result. /// /// Event sender /// Event arguments private void AddMap(object sender, RoutedEventArgs e) { OpenMapDialog dlg = new(); dlg.Owner = this; bool? result = dlg.ShowDialog(); if (result == false) return; switch (dlg.MapType) { case OpenMapDialog.FileType.ImageSequence: ViewModel.SetImageSeq(dlg.FilePath.Trim(), dlg.FileTemplate.Trim()); CreateXStackMenuItem.IsEnabled = true; CreateYStackMenuItem.IsEnabled = true; break; case OpenMapDialog.FileType.Tiff3D: ViewModel.SetTiff3D(dlg.FilePath.Trim()); break; case OpenMapDialog.FileType.Raw: ViewModel.SetRaw(dlg.FilePath.Trim(), dlg.ByteDepth, dlg.MapWidth, dlg.MapHeight, dlg.LittleEndian); CreateXStackMenuItem.IsEnabled = true; CreateYStackMenuItem.IsEnabled = true; break; default: break; } SetLevelSliders(); DefaultLevels(); CleanStacks(); checkXY.IsChecked = true; } /// /// Cleaning up the generated stacks after changing Map. /// private void CleanStacks() { checkZY.IsChecked = false; checkXZ.IsChecked = false; checkZY.IsEnabled = false; checkXZ.IsEnabled = false; ViewModel.CleanZYStack(); ViewModel.CleanXZStack(); } /// /// Opens configuration dialog and sets the new values /// /// /// private void OpenPreferences(object sender, RoutedEventArgs e) { ConfigurationDialog dlg = new(ViewModel.UserName, ViewModel.ZAxisColor, ViewModel.XAxisColor, ViewModel.YAxisColor, ViewModel.DataOnMapColor, ViewModel.DataOnMapHighlightColor); dlg.Title = "Preferences"; dlg.Owner = this; bool? result = dlg.ShowDialog(); if (result == false) return; ViewModel.UserName = dlg.UserName; ViewModel.ZAxisColor = dlg.ZColor; ViewModel.XAxisColor = dlg.XColor; ViewModel.YAxisColor = dlg.YColor; ViewModel.DataOnMapColor = dlg.DataOnMapColor; ViewModel.DataOnMapHighlightColor = dlg.DataHighlightColor; } /// /// Setting Map levels to defaults. /// private void DefaultLevels() { switch (ViewModel.BitDepth) { case 8: LevelMinBox.Text = "0"; LevelMaxBox.Text = byte.MaxValue.ToString(); break; case 16: LevelMinBox.Text = "0"; LevelMaxBox.Text = ushort.MaxValue.ToString(); break; case 32: LevelMinBox.Text = "0"; LevelMaxBox.Text = float.MaxValue.ToString("#"); break; default: break; } } /// /// Settings for sliders that change image Levels. /// Different max values and tick frequency depending /// on bit depth. /// private void SetLevelSliders() { switch (ViewModel.BitDepth) { case 8: LevelMinSlider.Maximum = byte.MaxValue; LevelMaxSlider.Maximum = byte.MaxValue; LevelMinSlider.TickFrequency = 1; LevelMaxSlider.TickFrequency = 1; break; case 16: LevelMinSlider.Maximum = ushort.MaxValue; LevelMaxSlider.Maximum = ushort.MaxValue; LevelMinSlider.TickFrequency = 1; LevelMaxSlider.TickFrequency = 1; break; case 32: //TODO: Need to calculate max values from // image's actual levels, otherwise // almost unusable. LevelMinSlider.Maximum = float.MaxValue; LevelMaxSlider.Maximum = float.MaxValue; LevelMinSlider.TickFrequency = 0.1; LevelMaxSlider.TickFrequency = 0.1; break; case 64: // Not supported. default: break; } } /// /// Calls viewmodel to save an already saved sample or open the the save /// dialog if the sample hasnt been saved yet. /// /// Event sender /// Event arguments private void SaveSample(object sender, RoutedEventArgs e) { if (ViewModel.IsSaved) return; if (!ViewModel.SavePathExists()) { OpenSaveAsSample(); return; } ViewModel.SaveSample(); } /// /// Opens save file dialog and calls viewmodel to save the sample to /// chosen filepath. /// /// Event sender /// Event arguments private void SaveAsSample(object sender, RoutedEventArgs e) { OpenSaveAsSample(); } /// /// Open dialog for saving a new file. /// private void OpenSaveAsSample() { SaveFileDialog saveFileDialog = new(); saveFileDialog.Filter = "JSON files (*.json)|*.json"; if (saveFileDialog.ShowDialog() == true) ViewModel.SaveAsSample(saveFileDialog.FileName); } /// /// Opens open file dialog and calls viewmodel to load a sample from the /// chosen filepath. /// /// Event sender /// Event arguments private void LoadSample(object sender, RoutedEventArgs e) { if (!AskToSaveChanges()) return; OpenFileDialog openFileDialog = new(); openFileDialog.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*"; openFileDialog.Title = "Opening Sample file"; if (openFileDialog.ShowDialog() == true) { ViewModel.LoadSample(openFileDialog.FileName); SetListViewFilter(); checkXY.IsChecked = false; checkZY.IsChecked = false; checkXZ.IsChecked = false; SetLevelSliders(); DefaultLevels(); } if (ViewModel.XYexists) { CreateXStackMenuItem.IsEnabled = true; CreateYStackMenuItem.IsEnabled = true; } } /// /// Flags to check if stack generation still in progress /// private bool generatingX = false; private bool generatingY = false; /// /// Event handler for creating X stack. /// /// Event sender. /// Event arguments. private async void CreateXStack_Click(object sender, RoutedEventArgs e) { SaveFileDialog stackFileDialog = new(); stackFileDialog.Filter = "Raw files (*.raw)|*.raw|All files (*.*)|*.*"; if (ViewModel.GetStackZPath() != "") { if (ViewModel.IsSequence()) { stackFileDialog.Filter = "All files (*.*)|*.*"; Directory.CreateDirectory( ViewModel.GetStackZPath() + "\\XStack\\"); stackFileDialog.InitialDirectory = System.IO.Path.GetFullPath(new Uri(ViewModel.GetStackZPath() + "\\XStack\\").LocalPath); } else { stackFileDialog.InitialDirectory = System.IO.Path.GetFullPath(new Uri(ViewModel.GetStackZPath()).LocalPath); } } stackFileDialog.FileName = ViewModel.GetStackZName() + "X"; if (stackFileDialog.ShowDialog() == true) { StackInProgressText.Visibility = Visibility.Visible; ZYstatusBlock.Visibility = Visibility.Visible; CreateXStackMenuItem.IsEnabled = false; try { generatingX = true; await ViewModel.CreateXStack(stackFileDialog.FileName); generatingX = false; } catch(StackWriteException) { generatingX = false; if(!generatingY) StackInProgressText.Visibility = Visibility.Collapsed; ZYstatusBlock.Visibility = Visibility.Collapsed; checkZY.IsEnabled = false; CreateXStackMenuItem.IsEnabled = true; throw; } } else { if (!generatingY) StackInProgressText.Visibility = Visibility.Collapsed; ZYstatusBlock.Visibility = Visibility.Collapsed; CreateXStackMenuItem.IsEnabled = true; return; } if (!generatingY) StackInProgressText.Visibility = Visibility.Collapsed; ZYstatusBlock.Visibility = Visibility.Collapsed; checkZY.IsEnabled = true; CreateXStackMenuItem.IsEnabled = true; } /// /// Event handler for creating Y stack. /// /// Event sender. /// Event arguments. private async void CreateYStack_Click(object sender, RoutedEventArgs e) { if (ViewModel.Is3DTiff()) { return; } SaveFileDialog stackFileDialog = new(); stackFileDialog.Filter = "All files (*.*)|*.*"; if (ViewModel.IsRaw()) stackFileDialog.Filter = "Raw files (*.raw)|*.raw|All files (*.*)|*.*"; if (ViewModel.GetStackZPath() != "") { if (ViewModel.IsSequence()) { stackFileDialog.Filter = "All files (*.*)|*.*"; Directory.CreateDirectory( ViewModel.GetStackZPath() + "\\YStack\\"); stackFileDialog.InitialDirectory = System.IO.Path.GetFullPath(new Uri(ViewModel.GetStackZPath() + "\\YStack\\").LocalPath); } else { stackFileDialog.InitialDirectory = System.IO.Path.GetFullPath(new Uri(ViewModel.GetStackZPath()).LocalPath); } } stackFileDialog.FileName = ViewModel.GetStackZName() + "Y"; if (stackFileDialog.ShowDialog() == true) { StackInProgressText.Visibility = Visibility.Visible; CreateYStackMenuItem.IsEnabled = false; XZstatusBlock.Visibility = Visibility.Visible; try { generatingY = true; await ViewModel.CreateYStack(stackFileDialog.FileName); generatingY = false; } catch(StackWriteException) { generatingY = false; if (!generatingX) StackInProgressText.Visibility = Visibility.Collapsed; XZstatusBlock.Visibility = Visibility.Collapsed; CreateYStackMenuItem.IsEnabled = true; checkXZ.IsEnabled = false; throw; } } else { if (!generatingX) StackInProgressText.Visibility = Visibility.Collapsed; XZstatusBlock.Visibility = Visibility.Collapsed; CreateYStackMenuItem.IsEnabled = true; return; } if(!generatingX) StackInProgressText.Visibility = Visibility.Collapsed; XZstatusBlock.Visibility = Visibility.Collapsed; CreateYStackMenuItem.IsEnabled = true; checkXZ.IsEnabled = true; } /// /// Event handler when a datapoint on the XY map is clicked. /// /// Event sender. /// Event arguments. private void XYsliceControl_DataPointMouseDown(object sender, MouseButtonEventArgs e) { Rectangle dp = (Rectangle)sender; AttachedData att = ViewModel.GetAttachmentByName(dp.Uid.ToString()); if (att == null) { MessageBox.Show("Something went wrong: Attached data not found"); return; } DisplayConnector(att); } /// /// Event handler when a datapoint on the ZY map is clicked. /// /// Event sender. /// Event arguments. private void ZYsliceControl_DataPointMouseDown(object sender, MouseButtonEventArgs e) { Rectangle dp = (Rectangle)sender; AttachedData att = ViewModel.GetAttachmentByName(dp.Uid.ToString()); if (att == null) { MessageBox.Show("Something went wrong: Attached data not found"); return; } DisplayConnector(att); } /// /// Event handler when a datapoint on the XZ map is clicked. /// /// Event sender. /// Event arguments. private void XZsliceControl_DataPointMouseDown(object sender, MouseButtonEventArgs e) { Rectangle dp = (Rectangle)sender; AttachedData att = ViewModel.GetAttachmentByName(dp.Uid.ToString()); if (att == null) { MessageBox.Show("Something went wrong: Attached data not found"); return; } DisplayConnector(att); } /// /// Handler for TextChanged on LevelMinBox. /// /// Event sender. Not used. /// Event arguments. Not used. private void LevelMinBox_TextChanged(object sender, TextChangedEventArgs e) { // Couldn't refactor this within reasonable timeframe. switch (ViewModel.BitDepth) { case 8: if(byte.TryParse(LevelMinBox.Text.Trim(), out byte min8)) { ViewModel.LevelMin = (double)min8 / ((double)byte.MaxValue * 1.0) * 100.0; LevelMinSlider.Value = min8; ShowDefaultBox(LevelMinBox); } else { ShowErrorBox(LevelMinBox, "Values between 0 and " + byte.MaxValue.ToString("#")); } break; case 16: if (ushort.TryParse(LevelMinBox.Text.Trim(), out ushort min16)) { ViewModel.LevelMin = (double)min16 / ((double)ushort.MaxValue * 1.0) * 100.0; LevelMinSlider.Value = min16; ShowDefaultBox(LevelMinBox); } else { ShowErrorBox(LevelMinBox, "Values between 0 and " + ushort.MaxValue.ToString("#")); } break; case 32: if (float.TryParse(LevelMinBox.Text.Trim(), out float min32)) { ViewModel.LevelMin = (double)min32 / ((double)float.MaxValue * 1.0) * 100.0; LevelMinSlider.Value = min32; ShowDefaultBox(LevelMinBox); } else { ShowErrorBox(LevelMinBox, "Values between " + float.MinValue.ToString("#") + " and " + float.MaxValue.ToString("#")); } break; case 64: // Not supported. default: break; } } /// /// Handler for TextChanged on LevelMaxBox. /// /// Event sender. Not used. /// Event arguments. Not used. private void LevelMaxBox_TextChanged(object sender, TextChangedEventArgs e) { // Couldn't refactor this within reasonable timeframe. switch (ViewModel.BitDepth) { case 8: if (byte.TryParse(LevelMaxBox.Text.Trim(), out byte max8)) { ViewModel.LevelMax = (double)max8 / ((double)byte.MaxValue * 1.0) * 100.0; LevelMaxSlider.Value = max8; ShowDefaultBox(LevelMaxBox); } else { ShowErrorBox(LevelMaxBox, "Values between 0 and " + byte.MaxValue.ToString("#")); } break; case 16: if (ushort.TryParse(LevelMaxBox.Text.Trim(), out ushort max16)) { ViewModel.LevelMax = (double)max16 / ((double)ushort.MaxValue * 1.0) * 100.0; LevelMaxSlider.Value = max16; ShowDefaultBox(LevelMaxBox); } else { ShowErrorBox(LevelMaxBox, "Values between 0 and " + ushort.MaxValue.ToString("#")); } break; case 32: if (float.TryParse(LevelMaxBox.Text.Trim(), out float max32) && max32 >= 0) { ViewModel.LevelMax = (double)max32 / ((double)float.MaxValue * 1.0) * 100.0; LevelMaxSlider.Value = max32; ShowDefaultBox(LevelMaxBox); } else { ShowErrorBox(LevelMinBox, "Values between " + float.MinValue.ToString("#") + " and " + float.MaxValue.ToString("#")); } break; case 64: // Not supported. default: break; } } /// /// Changes the style of textbox to ErrorTextBox and /// adds a tooltip. /// /// TextBox to be changed. /// Tooltip for the TextBox. private void ShowErrorBox(TextBox box, string tooltip) { box.Style = (Style)FindResource("ErrorTextBox"); box.ToolTip = tooltip; } /// /// Changes the style of textbox to DefaultTextBox and /// removes a tooltip. /// /// TextBox to be changed. private void ShowDefaultBox(TextBox box) { box.Style = (Style)FindResource("DefaultTextBox"); box.ToolTip = null; } /// /// Resetting Levels to default. /// /// Event sender. Not used. /// Event arguments. Not used. private void LevelResetButton_Click(object sender, RoutedEventArgs e) { ViewModel.LevelMin = 0.0; ViewModel.LevelMax = 100.0; DefaultLevels(); } /// /// Creating new sample. /// /// Event sender. Not used. /// Event arguments. Not used. private void NewSample(object sender, RoutedEventArgs e) { if (!AskToSaveChanges()) return; ViewModel.NewSample(); checkXY.IsChecked = false; checkZY.IsChecked = false; checkXZ.IsChecked = false; CreateXStackMenuItem.IsEnabled = false; CreateYStackMenuItem.IsEnabled = false; editModeMenuItem.IsChecked = false; SetListViewFilter(); } /// /// For closing the app from a command. /// /// /// private void CloseApp(object sender, RoutedEventArgs e) { Close(); } /// /// /// /// /// private void Window_Closing(object sender, CancelEventArgs e) { if (!AskToSaveChanges()) e.Cancel = true; if(e.Cancel != true) { if (!AskToSaveStack()) e.Cancel = true; } } /// /// /// /// private bool AskToSaveStack() { if (!ViewModel.GeneratedStacks()) return true; if (!ViewModel.SavePathExists()) { ViewModel.DeleteGeneratedStacks(); return true; } MessageBoxResult result = MessageBox.Show("Do you want to keep written stacks in hard drive?", "Keep stacks", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes ) return true; if (result == MessageBoxResult.Cancel) return false; ViewModel.DeleteGeneratedStacks(); return true; } /// /// Opens a messagebox that asks to save changes if there are unsaved changes. /// Saves on yes, doesn't save on no. /// Returns true if user did not cancel the action and wants the command /// to go through. /// /// True if user did not choose cancel on messagebox. private bool AskToSaveChanges() { if (ViewModel.IsSaved) return true; MessageBoxResult result = MessageBox.Show("There are unsaved changes. Do you want to save?", "Unsaved changes", MessageBoxButton.YesNoCancel); if (result == MessageBoxResult.Yes) { if (ViewModel.SavePathExists()) ViewModel.SaveSample(); else OpenSaveAsSample(); return true; } else if (result == MessageBoxResult.No) { return true; } return false; } /// /// Event handler for when LevelMinSlider value changes. /// /// Event sender. Not used. /// Event arguments. Not used. private void LevelMinSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (LevelMinSlider.Value == 0) LevelMinBox.Text = "0"; else LevelMinBox.Text = LevelMinSlider.Value.ToString("#"); } /// /// Event handler for when LevelMaxSlider value changes. /// /// Event sender. Not used. /// Event arguments. Not used. private void LevelMaxSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (LevelMaxSlider.Value == 0) LevelMaxBox.Text = "0"; else LevelMaxBox.Text = LevelMaxSlider.Value.ToString("#"); } /// /// Changes ShowAxes in ViewModel. /// /// /// private void ToggleShowAxes(object sender, RoutedEventArgs e) { ViewModel.ShowAxes = !ViewModel.ShowAxes; } /// /// Changes ShowDataOnMap in ViewModel. /// /// /// private void ToggleShowDataOnMap(object sender, RoutedEventArgs e) { ViewModel.ShowDataOnMap = !ViewModel.ShowDataOnMap; } /// /// Changes EditMode in ViewModel. /// /// /// private void ToggleEditMode(object sender, RoutedEventArgs e) { editModeMenuItem.IsChecked = !editModeMenuItem.IsChecked; } #region CanExecuteForCommands /// /// Can Save command be used. /// /// /// private void CommandBinding_CanSave(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = !ViewModel.IsSaved; } /// /// Command can always be executed. /// /// /// private void CommandBinding_CanAlwaysExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } /// /// Executes if EnableUI is true. /// /// /// private void CommandBinding_IsUIEnabled(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = ViewModel.EnableUI; } #endregion } }