/// 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
}
}