math-suite/Source/GraphingCalculator/MainWindow.xaml.cs

390 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace GraphingCalculator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Constructor
public MainWindow()
{
InitializeComponent();
// Update textboxes
graphingCanvas.CanvasBounds.BoundsChanged += new EventHandler(CanvasBounds_BoundsChanged);
graphingCanvas.CanvasBounds.Right = graphingCanvas.CanvasBounds.Right;
}
#endregion
#region Function list
private void updateList()
{
listExpressions.Items.Clear();
foreach (var i in graphingCanvas.Expressions)
{
CheckBox chk = new CheckBox();
chk.Content = i.ExpressionString;
chk.IsChecked = i.IsVisible;
chk.Foreground = new SolidColorBrush(i.Color);
chk.FontWeight = FontWeights.Bold;
chk.Click += new RoutedEventHandler(Visibility_Checked);
listExpressions.Items.Add(chk);
}
}
void Visibility_Checked(object sender, RoutedEventArgs e)
{
int index = listExpressions.Items.IndexOf(sender);
CheckBox c = sender as CheckBox;
if (index < 0 || c == null || !c.IsChecked.HasValue) return;
graphingCanvas.SetExpressionVisibility(index, c.IsChecked.Value);
}
private void buttonDelete_Click(object sender, RoutedEventArgs e)
{
int index = listExpressions.SelectedIndex;
if (index == -1) return;
listExpressions.Items.RemoveAt(index);
graphingCanvas.Expressions.RemoveAt(index);
graphingCanvas.EvaluateExpressions();
graphingCanvas.Redraw();
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
listExpressions.Items.Clear();
graphingCanvas.Expressions.Clear();
graphingCanvas.EvaluateExpressions();
graphingCanvas.Redraw();
}
private void buttonHideAll_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < listExpressions.Items.Count; i++)
{
CheckBox c = listExpressions.Items[i] as CheckBox;
if (c != null) c.IsChecked = false;
graphingCanvas.SetExpressionVisibility(i, false);
}
}
#endregion
#region Keyboard/mouse input
private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
{
graphingCanvas.PeformMouseWheelChange(e);
}
private bool EditingTextbox()
{
TextBox box = Keyboard.FocusedElement as TextBox;
return (box != null);
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
if (e.KeyboardDevice.Modifiers == ModifierKeys.None)
{
switch (e.Key)
{
case Key.F1: menuHelp_Click(this, new RoutedEventArgs()); break;
case Key.NumPad8:
case Key.Up:
if (!EditingTextbox()) graphingCanvas.PerformMoveUp();
else e.Handled = false;
break;
case Key.NumPad4:
case Key.Left:
if (!EditingTextbox()) graphingCanvas.PerformMoveLeft();
else e.Handled = false;
break;
case Key.NumPad6:
case Key.Right:
if (!EditingTextbox()) graphingCanvas.PerformMoveRight();
else e.Handled = false;
break;
case Key.NumPad2:
case Key.Down:
if (!EditingTextbox()) graphingCanvas.PerformMoveDown();
else e.Handled = false;
break;
case Key.NumPad5:
if (!EditingTextbox()) graphingCanvas.PerformReset();
else e.Handled = false;
break;
case Key.Add:
if (!EditingTextbox()) graphingCanvas.PerformZoomIn();
else e.Handled = false;
break;
case Key.Subtract:
if (!EditingTextbox()) graphingCanvas.PerformZoomOut();
else e.Handled = false;
break;
case Key.Enter: buttonPlot_Click(this, new RoutedEventArgs()); break;
default: e.Handled = false; break;
}
}
else if (Keyboard.Modifiers == ModifierKeys.Control)
{
if (e.Key == Key.F1) menuAbout_Click(this, new RoutedEventArgs());
else if (e.Key == Key.S) menuFileSave_Click(this, new RoutedEventArgs());
else if (e.Key == Key.O) menuFileImport_Click(this, new RoutedEventArgs());
else if (e.Key == Key.Q) menuFileExit_Click(this, new RoutedEventArgs());
else e.Handled = false;
}
else e.Handled = false;
}
#endregion
#region Buttons
private void buttonPlot_Click(object sender, RoutedEventArgs e)
{
if (inputExpression.Text == "") return;
VisualExpression expr = new VisualExpression();
try
{
expr.ExpressionString = inputExpression.Text;
expr.Process();
expr.Variables["x"] = expr.Variables["X"] = 0;
expr.Evaluate();
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error!");
#region Log
Log.LogEvent("Failed to plot expression '{0}'. Message: {1}", expr.ExpressionString, ex.Message);
Log.LogEvent("> Stack trace: {0}", ex.StackTrace);
#endregion
return;
}
graphingCanvas.AddExpression(expr);
updateList();
}
private void buttonEvaluate_Click(object sender, RoutedEventArgs e)
{
EvaluateWindow window = new EvaluateWindow(inputExpression.Text);
window.Show();
}
private void buttonIntegrate_Click(object sender, RoutedEventArgs e)
{
Controls.IntegralWindow window = new Controls.IntegralWindow(inputExpression.Text);
window.Show();
}
private bool IsNanInf (params double[] items)
{
bool result = false;
foreach (var i in items)
result = result || double.IsNaN(i) || double.IsInfinity(i);
return result;
}
private void buttonChangeBounds_Click(object sender, RoutedEventArgs e)
{
Expression el, er, et, eb;
double l, r, t, b;
bool ok = true;
// Initialize stuff
el = new Expression(inputBoundsLeft.Text);
er = new Expression(inputBoundsRight.Text);
et = new Expression(inputBoundsTop.Text);
eb = new Expression(inputBoundsBottom.Text);
l = r = t = b = 0;
// Initialize visuals
inputBoundsLeft.Background = SystemColors.WindowBrush;
inputBoundsTop.Background = SystemColors.WindowBrush;
inputBoundsRight.Background = SystemColors.WindowBrush;
inputBoundsBottom.Background = SystemColors.WindowBrush;
// Evaluate left
try { l = el.Evaluate(); }
catch {
if (!double.TryParse(el.ExpressionString, out l)) {
inputBoundsLeft.Background = Brushes.Pink; ok = false;
}
}
// Evaluate top
try { t = et.Evaluate(); }
catch {
if (!double.TryParse(et.ExpressionString, out t)) {
inputBoundsTop.Background = Brushes.Pink; ok = false;
}
}
// Evaluate right
try { r = er.Evaluate(); }
catch {
if (!double.TryParse(er.ExpressionString, out r)) {
inputBoundsRight.Background = Brushes.Pink; ok = false;
}
}
// Evaluate bottom
try { b = eb.Evaluate(); }
catch {
if (!double.TryParse(eb.ExpressionString, out b)) {
inputBoundsBottom.Background = Brushes.Pink; ok = false;
}
}
// Preview results
inputBoundsTop.Text = Math.Round(t, 15).ToString();
inputBoundsLeft.Text = Math.Round(l, 15).ToString();
inputBoundsRight.Text = Math.Round(r, 15).ToString();
inputBoundsBottom.Text = Math.Round(b, 15).ToString();
// Verify
if (ok)
{
if (l >= r || b >= t || IsNanInf(l, r, b, t)) MessageBox.Show("Invalid interval! Make sure left is smaller than right, bottom is smaller than top, and the intervals are finite.", "Error");
else graphingCanvas.SetCanvasBounds(new Bounds(l, t, r, b));
}
}
#endregion
#region Menu items
private void menuFileSave_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog save = new Microsoft.Win32.SaveFileDialog();
save.Filter = "XML Expression list|*.xml|All files|*.*";
save.Title = "Save expression list...";
bool? result = save.ShowDialog();
if (!result.HasValue || !result.Value) return;
IO.ExpressionFile file = new IO.ExpressionFile();
file.Expressions.AddRange(graphingCanvas.Expressions);
try { file.SaveToFile(save.FileName); }
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error!");
Log.LogEvent("Failed to save to file '{0}': {1}", save.FileName, ex.Message);
Log.LogEvent("> Stack trace: {0}", ex.StackTrace);
}
}
private void menuFileImport_Click(object sender, RoutedEventArgs e)
{
IO.ExpressionFile file = new IO.ExpressionFile();
Microsoft.Win32.OpenFileDialog open = new Microsoft.Win32.OpenFileDialog();
bool? res;
// Show dialog
open.Filter = "XML Expression list|*.xml|All files|*.*";
open.Title = "Import expression list";
res = open.ShowDialog();
if (!res.HasValue || !res.Value) return;
// Try to load data
try {
file.LoadFromFile(open.FileName);
}
catch (Exception ex) {
MessageBox.Show("Error: " + ex.Message, "Error!");
Log.LogEvent("Failed to import from file '{0}': {1}", open.FileName, ex.Message);
Log.LogEvent("> Stack trace: {0}", ex.StackTrace);
}
// Add expressions to our list
foreach (var i in file.Expressions)
this.graphingCanvas.AddExpression(i);
updateList();
}
private void menuFileExit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void menuAppLog_Click(object sender, RoutedEventArgs e)
{
new LogWindow().Show();
}
private void menuAppPref_Click(object sender, RoutedEventArgs e)
{
new SettingsWindow().ShowDialog();
graphingCanvas.SetCanvasBounds(graphingCanvas.CanvasBounds);
}
private void menuHelp_Click(object sender, RoutedEventArgs e)
{
DynamicLink.Launcher.StartModule("Help", "graphingcalc");
}
private void menuAbout_Click(object sender, RoutedEventArgs e)
{
DynamicLink.Launcher.About();
}
#endregion
#region Misc
void CanvasBounds_BoundsChanged(object sender, EventArgs e)
{
int p = GraphingCalculator.Properties.Settings.Default.RoundDoublesGraph;
this.inputBoundsBottom.Text = Math.Round(graphingCanvas.CanvasBounds.Bottom, p).ToString();
this.inputBoundsLeft.Text = Math.Round(graphingCanvas.CanvasBounds.Left, p).ToString();
this.inputBoundsTop.Text = Math.Round(graphingCanvas.CanvasBounds.Top, p).ToString();
this.inputBoundsRight.Text = Math.Round(graphingCanvas.CanvasBounds.Right, p).ToString();
#region Log
Log.LogEvent("Canvas bounds changed: left={0}; top={1}; right={2}; bottom={3}", this.inputBoundsLeft.Text,
this.inputBoundsTop.Text, this.inputBoundsRight.Text, this.inputBoundsBottom.Text);
#endregion
}
#endregion
}
}