math-suite/Source/MatrixCalculator/MainWindow.cs

813 lines
28 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MathNet.Numerics.LinearAlgebra;
namespace MatrixCalculator
{
public partial class MainWindow : Form
{
private WorksheetFile worksheet = new WorksheetFile();
private List<Matrix<double>> matrices = new List<Matrix<double>>();
private Matrix<double> matrixA, matrixB, matrixResult;
#region Matrix properties
private Matrix<double> MatrixA {
get { return matrixA; }
set {
matrixA = value;
GridViewHelper.PutMatrix(matrixA, dataMatrixA);
}
}
private Matrix<double> MatrixB
{
get { return matrixB; }
set
{
matrixB = value;
GridViewHelper.PutMatrix(matrixB, dataMatrixB);
}
}
private Matrix<double> MatrixResult
{
get { return matrixResult; }
set
{
matrixResult = value;
GridViewHelper.PutMatrix(matrixResult, dataResult);
}
}
private Matrix<double> MatrixPreview
{
get {
if (listMatrices.SelectedItems.Count == 1)
return matrices[listMatrices.SelectedIndices[0]];
else return null;
}
set {
GridViewHelper.PutMatrix(value, dataPreview);
}
}
#endregion
#region Constructor
public MainWindow()
{
InitializeComponent();
ResetAll();
}
#endregion
#region User interface
#region Menus
private void menuFileNew_Click(object sender, EventArgs e)
{
if (!ShowSaveWarningDialog()) return;
ResetAll();
worksheet = new WorksheetFile();
}
private void menuFileOpen_Click(object sender, EventArgs e)
{
if (!ShowSaveWarningDialog()) return;
// Open file dialog
string file;
if (!OpenDialog("Open worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return;
// Open the file
DataTable data = new DataTable();
try {
worksheet = new WorksheetFile(file);
data = worksheet.Read();
}
catch (Exception ex) {
MessageBox.Show("Error:" + ex.Message, "Error!");
return;
}
// Load data
ResetAll();
AddMatrices(data);
}
private void menuFileSave_Click(object sender, EventArgs e)
{
if (worksheet.FileName == "")
{
string file;
if (!SaveDialog("Save worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return;
worksheet.FileName = file;
}
try { worksheet.Save(); }
catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); }
}
private void menuFileSaveAs_Click(object sender, EventArgs e)
{
string file;
if (!SaveDialog("Save worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file)) return;
try { worksheet.SaveAs(file); }
catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error!"); }
}
private void menuFileImportCsv_Click(object sender, EventArgs e)
{
string file;
if (!OpenDialog("Open CSV file...", MatrixCalculator.Properties.Resources.CsvFormatFilter, out file))
return;
ImportCsvWindow wind = new ImportCsvWindow();
try { wind.LoadFile(file); }
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error!");
}
if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK)
return;
AddMatrix(wind.Matrix, wind.MatrixName, "", true);
}
private void menuFileImportWorksheet_Click(object sender, EventArgs e)
{
string file;
if (!OpenDialog("Open worksheet...", MatrixCalculator.Properties.Resources.WorksheetFormatFilter, out file))
return;
ImportWorksheetWindow wind = new ImportWorksheetWindow();
try { wind.LoadFile(file); }
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error!");
}
if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
AddMatrices(wind.Data);
}
private void menuFilePreferences_Click(object sender, EventArgs e)
{
new SettingsWindow().ShowDialog();
}
private void menuFileExit_Click(object sender, EventArgs e)
{
this.Close();
}
private void menuMatrixNew_Click(object sender, EventArgs e)
{
Editor edit = new Editor();
edit.Text = "New matrix...";
if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK)
return;
AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true);
}
private void menuMatrixEdit_Click(object sender, EventArgs e)
{
if (listMatrices.SelectedItems.Count != 1) return;
string old_name, new_name, desc;
Matrix<double> matrix;
// Set up variables
Editor edit = new Editor();
edit.Text = "Edit matrix...";
old_name = edit.MatrixName = listMatrices.SelectedItems[0].Text;
edit.MatrixDescription = listMatrices.SelectedItems[0].SubItems[1].Text;
edit.Matrix = matrices[listMatrices.SelectedIndices[0]];
// Dialog
if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
// Update new stuff
new_name = listMatrices.SelectedItems[0].Text = edit.MatrixName;
desc = listMatrices.SelectedItems[0].SubItems[1].Text = edit.MatrixDescription;
matrix = matrices[listMatrices.SelectedIndices[0]] = edit.Matrix;
// Update in file
worksheet.ModifyMatrix(old_name, new_name, CsvParser.ToCSV(matrix), desc);
// Update preview
listMatrices_SelectedIndexChanged(this, new EventArgs());
}
private void menuMatrixDelete_Click(object sender, EventArgs e)
{
foreach (int i in listMatrices.SelectedIndices)
{
worksheet.DeleteMatrix(listMatrices.Items[i].Text);
listMatrices.Items.RemoveAt(i);
matrices.RemoveAt(i);
}
}
private void menuMatrixDuplicate_Click(object sender, EventArgs e)
{
if (listMatrices.SelectedItems.Count != 1) return;
// Set up variables
Editor edit = new Editor();
edit.Text = "New matrix...";
edit.MatrixName = listMatrices.SelectedItems[0].Text;
edit.MatrixDescription = listMatrices.SelectedItems[0].SubItems[1].Text;
edit.Matrix = matrices[listMatrices.SelectedIndices[0]];
// Dialog
if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
// Update new stuff
AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true);
}
private void menuMatrixInsertA_Click(object sender, EventArgs e)
{
if (listMatrices.SelectedIndices.Count == 1)
MatrixA = matrices[listMatrices.SelectedIndices[0]];
}
private void menuMatrixInsertB_Click(object sender, EventArgs e)
{
if (listMatrices.SelectedIndices.Count == 1)
MatrixB = matrices[listMatrices.SelectedIndices[0]];
}
private void menuMatrixCopy_Click(object sender, EventArgs e)
{
if (listMatrices.SelectedIndices.Count != 1) return;
string csv = CsvParser.ToCSV(matrices[listMatrices.SelectedIndices[0]]);
Clipboard.SetText(csv, TextDataFormat.CommaSeparatedValue);
}
private void menuMatrixPaste_Click(object sender, EventArgs e)
{
// Get data
string csv = "";
if (Clipboard.ContainsText(TextDataFormat.CommaSeparatedValue))
csv = Clipboard.GetText(TextDataFormat.CommaSeparatedValue);
else return;
// Put it in a matrix
Editor edit = new Editor();
edit.Text = "Paste matrix...";
edit.Matrix = MatrixConverter.FromTable(CsvParser.GetTable(csv, ",;\t".ToArray()));
edit.MatrixName = "Pasted matrix";
if (edit.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
// Get data
AddMatrix(edit.Matrix, edit.MatrixName, edit.MatrixDescription, true);
}
private void menuMatrixPasteSpecial_Click(object sender, EventArgs e)
{
// Get data
string csv = "";
if (Clipboard.ContainsText(TextDataFormat.CommaSeparatedValue))
csv = Clipboard.GetText(TextDataFormat.CommaSeparatedValue);
else if (Clipboard.ContainsText())
csv = Clipboard.GetText();
else return;
// Use CSV importer
ImportCsvWindow wind = new ImportCsvWindow();
wind.Csv = csv;
if (wind.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
AddMatrix(wind.Matrix, wind.MatrixName, "", true);
}
private void menuHelpAbout_Click(object sender, EventArgs e)
{
DynamicLink.Launcher.About();
}
private void menuHelpHelp_Click(object sender, EventArgs e)
{
DynamicLink.Launcher.StartModule("Help", "matrixcalc");
}
private void contextMenuDataAdd_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var menu = item.Owner as ContextMenuStrip;
var grid = menu.SourceControl as DataGridView;
if (grid == null) return;
// Create editor window
Editor editwindow = new Editor();
editwindow.Text = "New matrix...";
editwindow.Matrix = GridViewHelper.GetMatrix(grid);
if (editwindow.ShowDialog() == System.Windows.Forms.DialogResult.OK)
AddMatrix(editwindow.Matrix, editwindow.MatrixName, editwindow.MatrixDescription, true);
}
private void contextMenuDataClear_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var menu = item.Owner as ContextMenuStrip;
var grid = menu.SourceControl as DataGridView;
if (grid == null) return;
if (grid == dataMatrixA) matrixA = null;
else if (grid == dataMatrixB) matrixB = null;
grid.Rows.Clear();
grid.Columns.Clear();
}
private void contextMenuDataCopy_Click(object sender, EventArgs e)
{
var item = sender as ToolStripMenuItem;
var menu = item.Owner as ContextMenuStrip;
var grid = menu.SourceControl as DataGridView;
if (grid == null) return;
string csv = CsvParser.ToCSV(grid);
Clipboard.SetText(csv, TextDataFormat.CommaSeparatedValue);
}
#endregion
#region Calculator buttons
private void buttonAdd_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 1, 1, 1, 0, 0)) return;
try { MatrixResult = matrixA + matrixB; }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonSub_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 1, 1, 1, 0, 0)) return;
try { MatrixResult = matrixA - matrixB; }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonMul_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 1, 1, 0, 1, 0)) return;
try { MatrixResult = matrixA * matrixB; }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonAddNum_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
// Get a number
NumericInput input = new NumericInput(NumericInput.NumberType.Real);
if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
double number = input.NumberReal;
// Now add it
Matrix<double> m = Matrix<double>.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, number);
MatrixResult = matrixA + m;
}
private void buttonSubNum_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
// Get a number
NumericInput input = new NumericInput(NumericInput.NumberType.Real);
if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
double number = input.NumberReal;
// Now add it
Matrix<double> m = Matrix<double>.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, number);
MatrixResult = matrixA - m;
}
private void buttonMulNum_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
// Get a number
NumericInput input = new NumericInput(NumericInput.NumberType.Real);
if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
double number = input.NumberReal;
// Now add it
MatrixResult = matrixA * number;
}
private void buttonInv_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
try { MatrixResult = matrixA.Inverse(); }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonPow_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
// Get a number
NumericInput input = new NumericInput(NumericInput.NumberType.Integer);
if (input.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
int number = input.NumberInt;
// Calculate
try { MatrixResult = MatrixHelper.Power (matrixA, number); }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonTransp_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
try { MatrixResult = matrixA.Transpose(); }
catch (Exception ex)
{
textOutput.Text = "Error: " + ex.Message;
}
}
private void buttonTrace_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix)
MatrixResult = Matrix<double>.Build.Dense(1, 1, matrixA.Trace());
else textOutput.Text = MyDouble.String(matrixA.Trace());
}
private void buttonRank_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix)
MatrixResult = Matrix<double>.Build.Dense(1, 1, Convert.ToDouble(matrixA.Rank()));
else textOutput.Text = matrixA.Rank().ToString();
}
private void buttonDet_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 1)) return;
if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix)
MatrixResult = Matrix<double>.Build.Dense(1, 1, matrixA.Determinant());
else textOutput.Text = MyDouble.String(matrixA.Determinant());
}
private void buttonMin_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix)
MatrixResult = Matrix<double>.Build.Dense(1, 1, MatrixHelper.Min(matrixA));
else textOutput.Text = MyDouble.String(MatrixHelper.Min(matrixA));
}
private void buttonMax_Click(object sender, EventArgs e)
{
if (!VerifyConditions(1, 0, 0, 0, 0, 0)) return;
if (MatrixCalculator.Properties.Settings.Default.NumberResultAsMatrix)
MatrixResult = Matrix<double>.Build.Dense(1, 1, MatrixHelper.Max(matrixA));
else textOutput.Text = MyDouble.String(MatrixHelper.Max(matrixA));
}
#endregion
#region Drag & drop
private void dataMatrix_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(ListViewItem))) e.Effect = DragDropEffects.Move;
else e.Effect = DragDropEffects.None;
}
private void dataMatrix_DragDrop(object sender, DragEventArgs e)
{
ListViewItem data = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
var send = sender as DataGridView;
if (data == null || send == null) return;
if (send == dataMatrixA) MatrixA = matrices[data.Index];
else if (send == dataMatrixB) MatrixB = matrices[data.Index];
}
private void listMatrices_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listMatrices_MouseDown(object sender, MouseEventArgs e)
{
if (listMatrices.SelectedItems.Count == 1 && e.Button == System.Windows.Forms.MouseButtons.Left)
listMatrices.DoDragDrop(listMatrices.SelectedItems[0], DragDropEffects.Move);
}
#endregion
#region List Matrices Selected Index Changed
private void EnableMenus(int insA, int insB, int copy, int edit, int del, int dupl)
{
menuMatrixInsertA.Enabled = contextMenuListInsertA.Enabled = (insA != 0);
menuMatrixInsertB.Enabled = contextMenuListInsertB.Enabled = (insB != 0);
menuMatrixCopy.Enabled = contextMenuListCopy.Enabled = (copy != 0);
menuMatrixEdit.Enabled = contextMenuListEdit.Enabled = (edit != 0);
menuMatrixDelete.Enabled = contextMenuListDelete.Enabled = (del != 0);
menuMatrixDuplicate.Enabled = contextMenuListDuplicate.Enabled = (dupl != 0);
}
private void listMatrices_SelectedIndexChanged(object sender, EventArgs e)
{
switch (listMatrices.SelectedIndices.Count)
{
case 0: EnableMenus(0, 0, 0, 0, 0, 0); break;
case 1:
this.labelPreviewName.Text = "Name: " + listMatrices.SelectedItems[0].Text;
this.labelPreviewDesc.Text = "Description: " + listMatrices.SelectedItems[0].SubItems[1].Text;
MatrixPreview = matrices[listMatrices.SelectedIndices[0]];
EnableMenus(1, 1, 1, 1, 1, 1);
break;
default: EnableMenus(0, 0, 0, 0, 1, 0); break;
}
}
#endregion
#region Painting
protected override void OnPaintBackground(PaintEventArgs e)
{
DynamicLink.Controls.BackgroundGradient.Paint(e.Graphics, new Rectangle(-1, -1, this.Width, this.Height));
}
#endregion
#region Others
private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
{
if (!ShowSaveWarningDialog()) e.Cancel = true;
if (!e.Cancel) MatrixCalculator.Properties.Settings.Default.Save();
}
private void textOutput_TextChanged(object sender, EventArgs e)
{
toolTip.SetToolTip(textOutput, textOutput.Text);
}
#endregion
#endregion
#region Additional routines
private string MakeUnique(string name)
{
List<string> probl = new List<string>();
// Test name uniqueness
bool unique = true;
foreach (ListViewItem i in this.listMatrices.Items)
{
if (i.Text == name) unique = false;
if (i.Text.StartsWith(name)) probl.Add(i.Text);
}
// If not unique, generate a unique name
if (!unique)
{
int n = 0;
while (probl.Contains(name + n.ToString()) && n < 10000) n++;
name += n.ToString();
}
return name;
}
private void AddMatrix(Matrix<double> m, string name, string desc, bool inFile = false)
{
matrices.Add(m);
name = MakeUnique(name);
// Add it
ListViewItem item = listMatrices.Items.Add(name);
item.SubItems.Add(desc);
// To file?
if (inFile) worksheet.AddMatrix(name, CsvParser.ToCSV(m), desc);
}
private void AddMatrices(DataTable data, bool infile = false)
{
AddMatrices(data, infile, false, MatrixCalculator.Properties.Settings.Default.InternalCsvSeparators.ToArray());
}
private void AddMatrices(DataTable data, bool infile, bool combine, params char[] separs)
{
foreach (DataRow i in data.Rows)
{
try {
string name = i["name"].ToString();
string desc = i["description"].ToString();
string csv = i["csv"].ToString();
DataTable dt = CsvParser.GetTable(csv, separs, combine);
AddMatrix(MatrixConverter.FromTable(dt), name, desc, infile);
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.Message, "Error!");
return;
}
}
}
private void ResetAll()
{
matrices.Clear();
listMatrices.Items.Clear();
AddMatrix(Matrix<double>.Build.Dense(7, 7, 0), "Zero", "Zero matrix");
AddMatrix(Matrix<double>.Build.DenseIdentity(7, 7), "One", "Identity matrix");
}
private bool ShowSaveWarningDialog()
{
// Nothing to do
if (worksheet.IsQueueEmpty) return true;
// Show dialog
switch (MessageBox.Show("All the unsaved changes will be lost. Do you want to save the current worksheet?",
"Save?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
{
case System.Windows.Forms.DialogResult.Cancel: return false;
case System.Windows.Forms.DialogResult.Yes:
menuFileSave_Click(this, new EventArgs());
break;
case System.Windows.Forms.DialogResult.No:
break;
}
return true;
}
private bool OpenDialog(string title, string filter, out string filename)
{
bool result;
// Initialize dialog
OpenFileDialog open = new OpenFileDialog();
open.Title = title;
open.Filter = filter;
// Show
if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK) result = true;
else result = false;
// Output
filename = open.FileName;
return result;
}
private bool SaveDialog(string title, string filter, out string filename)
{
bool result;
// Initialize dialog
SaveFileDialog save = new SaveFileDialog();
save.Title = title;
save.Filter = filter;
// Show
if (save.ShowDialog() == System.Windows.Forms.DialogResult.OK) result = true;
else result = false;
// Output
filename = save.FileName;
return result;
}
#endregion
#region Additional calculator routines
bool VerifyConditions(int includeA, int includeB, int changeSpecial, int checkSum, int checkMul, int checkSq)
{
// Verify matrix A
if (includeA != 0 && matrixA == null)
{
textOutput.Text = "Error: Missing matrix A!";
return false;
}
// Verify matrix B
if (includeB != 0 && matrixB == null)
{
textOutput.Text = "Error: Missing matrix B!";
return false;
}
// Replace special matrix types (0 and identity)
if (changeSpecial != 0)
{
// Matrix A
if (MatrixHelper.Equal(matrixA, Matrix<double>.Build.Dense(7, 7, 0)))
{
if (checkSum != 0) matrixA = Matrix<double>.Build.Dense(matrixB.RowCount, matrixB.ColumnCount, 0.0);
if (checkMul != 0) matrixA = Matrix<double>.Build.Dense(matrixB.ColumnCount, matrixB.ColumnCount, 0.0);
}
if (MatrixHelper.Equal(matrixA, Matrix<double>.Build.DenseIdentity(7, 7)))
{
if (checkSum != 0) matrixA = Matrix<double>.Build.DenseIdentity(matrixB.RowCount, matrixB.ColumnCount);
if (checkMul != 0) matrixA = Matrix<double>.Build.DenseIdentity(matrixB.ColumnCount, matrixB.ColumnCount);
}
// Matrix B
if (MatrixHelper.Equal(matrixB, Matrix<double>.Build.Dense(7, 7, 0)))
{
if (checkSum != 0) matrixB = Matrix<double>.Build.Dense(matrixA.RowCount, matrixA.ColumnCount, 0.0);
if (checkMul != 0) matrixB = Matrix<double>.Build.Dense(matrixA.ColumnCount, matrixA.ColumnCount, 0.0);
}
if (MatrixHelper.Equal(matrixB, Matrix<double>.Build.DenseIdentity(7, 7)))
{
if (checkSum != 0) matrixB = Matrix<double>.Build.DenseIdentity(matrixA.RowCount, matrixA.ColumnCount);
if (checkMul != 0) matrixB = Matrix<double>.Build.DenseIdentity(matrixA.ColumnCount, matrixA.ColumnCount);
}
}
// If sum, make sure sizes match
if (checkSum != 0 && (matrixA.RowCount != matrixB.RowCount || matrixA.ColumnCount != matrixB.ColumnCount))
{
textOutput.Text = "Error: Incompatible matrices.";
return false;
}
// If multiplication, check A's width = B's height
if (checkMul != 0 && (matrixA.ColumnCount != matrixB.RowCount))
{
textOutput.Text = "Error: Incompatible matrices.";
return false;
}
// If matrix A must be square
if (checkSq != 0 && (matrixA.ColumnCount != matrixA.RowCount))
{
textOutput.Text = "Error: Invalid matrix.";
return false;
}
return true;
}
#endregion
}
}