private void OnDuplicateButtonClick(object sender, EventArgs e) { ListView.Item selected = listView.FocusedItem; if (selected != null && selected.NumericValueSource is ArithmeticExpression) { ArithmeticExpression ae = ArithmeticExpression.Parse(selected.Text); ae.Callbacks = selected.OperatorCallbacks; ListView.Item item = new ListView.Item { OperatorCallbacks = selected.OperatorCallbacks, Color = FindNewColor(), CheckState = selected.CheckState, CheckEnabled = selected.CheckEnabled, ImageResourceCallback = OnImageResourceCallback, Description = selected.Description, NumericValueSource = ae, Text = selected.Text, TextDisplay = selected.TextDisplay }; listView.Items.Add(item); listView.FocusedItem = item; listView.Invalidate(); listView.Update(); listView.EnsureVisible(item); } }
private void OnItemBeforeEdit(object sender, CancelEventArgs e) { ListView.Item item = (ListView.Item)sender; if (item.NumericValueSource is MemoryDataSet || item.NumericValueSource is FileDataSet) { e.Cancel = true; } }
private void RefreshUI() { ListView.Item selection = listView.SelectedItem; double value; findButton.Enabled = (selection?.NumericValueSource != null && !string.IsNullOrWhiteSpace(valueTextBox.Text) && double.TryParse(valueTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out value) && value != 0); }
private void OnSetItemCallbacksButtonClick(object sender, EventArgs e) { ToolStripMenuItem item = sender as ToolStripMenuItem; if (item == null) { return; } ListView.Item selected = listView.FocusedItem; if (selected != null && !(selected.NumericValueSource is MemoryDataSet) && !(selected.NumericValueSource is FileDataSet)) { selected.OperatorCallbacks = item.Tag as OperatorCallbacks; } }
private void OnFindButtonClick(object sender, EventArgs e) { ListView.Item selection = listView.SelectedItem; if (selection?.NumericValueSource != null) { double x; if (!double.TryParse(valueTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out x) || x == 0) { return; } double y = selection.NumericValueSource.Evaluate(x); graph.ZoomToValue(x, y); Close(); } }
private void OnRemoveButtonClick(object sender, EventArgs e) { ListView.Item selected = listView.FocusedItem; if (selected != null) { int index = selected.Index; listView.Items.Remove(selected); if (listView.Items.Count > 0) { listView.FocusedItem = listView.Items[Math.Max(0, index - 1)]; } IDisposable sourceDisposable = selected.NumericValueSource as IDisposable; if (sourceDisposable != null) { sourceDisposable.Dispose(); } } }
private void OnAddButtonClick(object sender, EventArgs e) { ListView.Item item = new ListView.Item { OperatorCallbacks = defaultDoubleCallbacks, Color = FindNewColor(), CheckState = CheckState.Checked, CheckEnabled = false, ImageResourceCallback = OnImageResourceCallback, TextDisplay = "\f[0x999999]" + Tx.T("main.enter expression") }; OnItemPropertyChanged(item, new PropertyChangedEventArgs("OperatorCallbacks")); listView.Items.Add(item); listView.FocusedItem = item; listView.Invalidate(); listView.Update(); listView.EnsureVisible(item); }
/// <summary> /// Refreshes state of toolstrip /// </summary> private void RefreshToolStrip() { ListView.Item selection = listView.SelectedItem; if (selection == null) { removeButton.Enabled = false; duplicateButton.Enabled = false; exportButton.Enabled = false; implementationButton.Enabled = false; } else { removeButton.Enabled = true; ArithmeticExpression arithmeticExpression = selection.NumericValueSource as ArithmeticExpression; if (arithmeticExpression != null) { duplicateButton.Enabled = true; exportButton.Enabled = true; implementationButton.Enabled = true; foreach (ToolStripItem item in implementationButton.DropDownItems) { if (item.Tag != null) { ((ToolStripMenuItem)item).Checked = (item.Tag == arithmeticExpression.Callbacks); } } } else { duplicateButton.Enabled = false; exportButton.Enabled = false; implementationButton.Enabled = false; } } }
private void OnImportButtonClick(object sender, EventArgs e) { using (OpenFileDialog dialog = new OpenFileDialog()) { const string ext = ".fvis-values"; dialog.CheckFileExists = true; dialog.Filter = Tx.T("dataset.filetype") + " (*" + ext + ")|*" + ext; dialog.FilterIndex = 0; dialog.RestoreDirectory = true; dialog.Title = Tx.T("dataset.load"); if (dialog.ShowDialog(this) != DialogResult.OK) { return; } string filename = dialog.FileName; using (FileStream s = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) using (BinaryReader br = new BinaryReader(s)) { byte[] header = br.ReadBytes(4); if (header[0] != 'f' || header[1] != 'V' || header[2] != 'i' || header[3] != 's') { MessageBox.Show(this, Tx.T("dataset.errors.file not valid"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } int flags = br.ReadInt32(); string description = br.ReadString(); long axisX = br.ReadInt64(); long axisY = br.ReadInt64(); double scaleFactor = br.ReadDouble(); ulong count = br.ReadUInt64(); if (count <= 0) { MessageBox.Show(this, Tx.T("dataset.errors.file not valid"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } ulong offset = (ulong)s.Position; // Close stream and open the same file in memory-mapped mode s.Close(); FileDataSet fileDataSet = new FileDataSet(filename, offset, count); // Add item to ListView StringBuilder sb = new StringBuilder(); sb.Append("\f[I]\f[0x888888]" + Tx.T("dataset.part of function") + " \f[Rc]\f[I]"); int i = description.IndexOf('#'); if (i != -1) { while (i > 0 && description[i - 1] == ' ') { i--; } sb.Append(description, 0, i); sb.Append("\f[0x498E3E]"); sb.Append(description, i, description.Length - i); } else { sb.Append(description); } ListView.Item item = new ListView.Item { NumericValueSource = fileDataSet, Text = description, TextDisplay = sb.ToString(), Color = FindNewColor(), CheckState = CheckState.Checked, CheckEnabled = true, ImageResourceCallback = OnImageResourceCallback }; listView.Items.Add(item); listView.FocusedItem = item; listView.Invalidate(); listView.Update(); listView.EnsureVisible(item); // Zoom to values in graph graph.ScaleFactor = scaleFactor; graph.AxisX = axisX; graph.AxisY = axisY; } } }
private void OnImplementationAddButtonClick(object sender, EventArgs e) { ListView.Item selected = listView.FocusedItem; if (selected == null) { return; } using (OpenFileDialog dialog = new OpenFileDialog()) { dialog.CheckFileExists = true; dialog.Filter = Tx.T("mathlib.filetype") + " (*.dll)|*.dll"; dialog.FilterIndex = 0; dialog.RestoreDirectory = true; dialog.Title = Tx.T("mathlib.load"); if (dialog.ShowDialog(this) != DialogResult.OK) { return; } string filename = dialog.FileName; try { NativeOperatorCallbacks callbacks = new NativeOperatorCallbacks(filename); ToolStripMenuItem item = new ToolStripMenuItem(callbacks.ImplementationName); item.ShortcutKeyDisplayString = "(" + Path.GetFileName(filename) + ")"; item.Click += OnSetItemCallbacksButtonClick; item.Tag = callbacks; string missingCallbacks = ConvertMissingCallbacksToString(callbacks); if (!string.IsNullOrEmpty(missingCallbacks)) { item.ToolTipText = Tx.T("mathlib.missing callbacks") + missingCallbacks; } implementationButton.DropDownItems.Add(item); OnSetItemCallbacksButtonClick(item, EventArgs.Empty); } catch (BadImageFormatException) { if (Environment.Is64BitProcess) { try { NativeOperatorRemotingCallbacks callbacks = new NativeOperatorRemotingCallbacks(filename); ToolStripMenuItem item = new ToolStripMenuItem(callbacks.ImplementationName); item.ShortcutKeyDisplayString = "(x86, " + Path.GetFileName(filename) + ")"; item.Click += OnSetItemCallbacksButtonClick; item.Tag = callbacks; string missingCallbacks = ConvertMissingCallbacksToString(callbacks); if (!string.IsNullOrEmpty(missingCallbacks)) { item.ToolTipText = Tx.T("mathlib.missing callbacks") + missingCallbacks; } implementationButton.DropDownItems.Add(item); OnSetItemCallbacksButtonClick(item, EventArgs.Empty); } catch (InvalidOperationException) { MessageBox.Show(this, Tx.T("mathlib.errors.not valid"), Tx.T("mathlib.errors.title"), MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { MessageBox.Show(this, ex.Message, Tx.T("mathlib.errors.title"), MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { MessageBox.Show(this, Tx.T("mathlib.errors.platform mismatch"), Tx.T("mathlib.errors.title"), MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (InvalidOperationException) { MessageBox.Show(this, Tx.T("mathlib.errors.not valid"), Tx.T("mathlib.errors.title"), MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { MessageBox.Show(this, ex.Message, Tx.T("mathlib.errors.title"), MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
/// <summary> /// Checks missing callbacks for given arithmetic expression /// </summary> /// <param name="item">Item to check</param> /// <param name="text">String builder to append warings to</param> private void CheckCallbacks(ListView.Item item, StringBuilder text) { if (item?.OperatorCallbacks == null) { return; } ArithmeticExpression ae = item.NumericValueSource as ArithmeticExpression; if (ae == null) { return; } bool isFirst = true; bool iconAppended = false; string[] missingCallbacks = null; NativeOperatorCallbacks callbacks = item.OperatorCallbacks as NativeOperatorCallbacks; if (callbacks != null) { missingCallbacks = callbacks.MissingCallbacks; if (missingCallbacks.Length == 0) { return; } } else { NativeOperatorRemotingCallbacks proxyCallbacks = item.OperatorCallbacks as NativeOperatorRemotingCallbacks; if (proxyCallbacks != null) { iconAppended = true; text.Append(" \f[-]\f[image:warning] \f[0xaa6400]\f[I]" + Tx.T("mathlib.remoting")); missingCallbacks = proxyCallbacks.MissingCallbacks; if (missingCallbacks.Length == 0) { text.Append("\f[I]\f[Rc]"); return; } } } if (missingCallbacks == null) { return; } for (int i = 0; i < missingCallbacks.Length; i++) { string current = missingCallbacks[i]; if (!ae.IsCallbackUsed(current)) { continue; } if (isFirst) { isFirst = false; if (iconAppended) { text.Append(" \f[0x999999]|\f[0xaa6400] " + Tx.T("mathlib.missing callbacks")); } else { text.Append(" \f[-]\f[image:warning] \f[0xaa6400]\f[I]" + Tx.T("mathlib.missing callbacks")); } } else { text.Append(", "); } if (current.StartsWith("constant_", StringComparison.Ordinal)) { text.Append("\f[I]"); text.Append(current, 9, current.Length - 9); text.Append("\f[I]"); } else if (current.StartsWith("operator_", StringComparison.Ordinal)) { text.Append(current, 9, current.Length - 9); } else { text.Append(current); } } if (!isFirst) { text.Append("\f[I]\f[Rc]"); } }
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Text") { ListView.Item item = (ListView.Item)sender; if (string.IsNullOrEmpty(item.Text)) { item.NumericValueSource = null; item.TextDisplay = "\f[0x999999]" + Tx.T("main.enter expression"); } else { try { ArithmeticExpression ae = ArithmeticExpression.Parse(item.Text); ae.Callbacks = item.OperatorCallbacks; item.NumericValueSource = ae; int i = item.Text.IndexOf('#'); if (i != -1) { while (i > 0 && item.Text[i - 1] == ' ') { i--; } StringBuilder sb = new StringBuilder(); sb.Append(item.Text, 0, i); if (ae.VariableName == null && !ae.IsSimpleConstantOnly) { double result = ae.Evaluate(double.NaN); sb.Append("\f[I]\f[0x999999] = "); sb.Append(result.ToExactString().LimitSize(32)); sb.Append(" \f[I]"); } sb.Append("\f[0x498E3E]"); sb.Append(item.Text, i, item.Text.Length - i); CheckCallbacks(item, sb); item.TextDisplay = sb.ToString(); } else { if (ae.VariableName == null && !ae.IsSimpleConstantOnly) { StringBuilder sb = new StringBuilder(); sb.Append(item.Text); double result = ae.Evaluate(double.NaN); sb.Append("\f[I]\f[0x999999] = "); if (double.IsPositiveInfinity(result)) { sb.Append("+∞"); } else if (double.IsNegativeInfinity(result)) { sb.Append("−∞"); } else { sb.Append(result.ToExactString().LimitSize(32)); } sb.Append("\f[I]"); CheckCallbacks(item, sb); item.TextDisplay = sb.ToString(); } else { StringBuilder sb = new StringBuilder(); sb.Append(item.Text); CheckCallbacks(item, sb); item.TextDisplay = sb.ToString(); } } } catch (SyntaxException ex) { item.NumericValueSource = null; StringBuilder sb = new StringBuilder(); sb.Append("\f[0x8b0000]"); int length = ex.Input.IndexOf('#'); if (length == -1) { length = ex.Input.Length; } if (length < 3) { sb.Append(ex.Input); } else { if (ex.Index - 1 < 0) { sb.Append("\f[U]"); } else { sb.Append(ex.Input, 0, ex.Index - 1); sb.Append("\f[U]\f[0xd00000]"); sb.Append(ex.Input[ex.Index - 1]); } sb.Append("\f[0xff0000]"); sb.Append(ex.Input[ex.Index]); if (ex.Index + 1 < length) { sb.Append("\f[0xd00000]"); sb.Append(ex.Input[ex.Index + 1]); sb.Append("\f[0x8b0000]\f[U]"); if (ex.Index + 2 < length) { sb.Append(ex.Input, ex.Index + 2, length - (ex.Index + 2)); } } else { sb.Append("\f[0xd00000] \f[U]"); } } switch (ex.ExceptionType) { case SyntaxException.Type.Unknown: break; case SyntaxException.Type.InvalidNumber: sb.Append(" \f[-]\f[image:warning] \f[0xaa6400]\f[I]"); sb.Append(Tx.T("expression.errors.invalid number")); break; case SyntaxException.Type.DistinctVariableCountExceeded: sb.Append(" \f[-]\f[image:warning] \f[0xaa6400]\f[I]"); sb.Append(Tx.T("expression.errors.distinct variable count exceeded")); break; case SyntaxException.Type.ParenthesesCountMismatch: sb.Append(" \f[-]\f[image:warning] \f[0xaa6400]\f[I]"); sb.Append(Tx.T("expression.errors.parentheses count mismatch")); break; default: throw new InvalidEnumArgumentException("ex.ExceptionType", (int)ex.ExceptionType, typeof(SyntaxException.Type)); } item.TextDisplay = sb.ToString(); } catch (Exception ex) { item.NumericValueSource = null; item.TextDisplay = "\f[0x8b0000]" + item.Text + " \f[-]\f[image:warning] \f[0xaa6400]\f[I]" + ex.Message; } } item.CheckEnabled = (item.NumericValueSource != null); RefreshToolStrip(); } else if (e.PropertyName == "OperatorCallbacks") { ListView.Item item = (ListView.Item)sender; ArithmeticExpression arithmeticExpression = item.NumericValueSource as ArithmeticExpression; if (arithmeticExpression != null) { arithmeticExpression.Callbacks = item.OperatorCallbacks; OnItemPropertyChanged(item, new PropertyChangedEventArgs("Text")); } item.Description = item.OperatorCallbacks.ImplementationName; } }
private unsafe void OnExportSelectedButtonClick(object sender, EventArgs e) { ListView.Item selected = listView.FocusedItem; if (selected == null) { // Nothing to export... return; } ArithmeticExpression ae = selected.NumericValueSource as ArithmeticExpression; if (ae == null) { MessageBox.Show(this, Tx.T("dataset.errors.item not supported"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } long xFi = 0, xLi = 0, distance = 0; if (ae.VariableName != null) { double xF_ = graph.MinVisibleX; double xL_ = graph.MaxVisibleX; if (xF_ >= xL_) { MessageBox.Show(this, Tx.T("dataset.errors.cannot determine interval"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (Math.Sign(xF_) != Math.Sign(xL_)) { MessageBox.Show(this, Tx.T("dataset.errors.interval with zero"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // Swap first and last for negative "x" values xFi = *(long *)&xF_; xLi = *(long *)&xL_; if (xFi > xLi) { long swap = xLi; xLi = xFi; xFi = swap; } distance = (xLi - xFi); if (distance < 0) { MessageBox.Show(this, Tx.T("dataset.errors.interval too big"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (distance > 10 * 1000 * 1000) { long sizeInMB = (distance * 8 * 2 / 1024 / 1024); string sizeString; if (sizeInMB > 100L * 1024L) { sizeString = Tx.N(sizeInMB / 1024) + " GB"; } else { sizeString = Tx.N(sizeInMB) + " MB"; } if (MessageBox.Show(this, Tx.T("dataset.errors.file too big", Tx.N(distance), sizeString), Tx.T("main.warning"), MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK) { return; } } } using (SaveFileDialog dialog = new SaveFileDialog()) { const string ext = ".fvis-values"; dialog.Filter = Tx.T("dataset.filetype") + " (*" + ext + ")|*" + ext; dialog.FilterIndex = 0; dialog.RestoreDirectory = true; dialog.Title = Tx.T("dataset.save"); string filename = selected.Text; char[] invalidChars = Path.GetInvalidFileNameChars(); for (int i = 0; i < invalidChars.Length; i++) { filename = filename.Replace(invalidChars[i], '_'); } dialog.FileName = filename + ext; if (dialog.ShowDialog(this) != DialogResult.OK) { return; } filename = dialog.FileName; ProgressDialog progressDialog = new ProgressDialog(); progressDialog.Text = Tx.T("dataset.saving.title"); progressDialog.MainInstruction = Tx.T("dataset.saving.description"); progressDialog.Line1 = Tx.T("dataset.saving.progress", "0", Tx.N(xLi - xFi)); progressDialog.Show(this); ThreadPool.UnsafeQueueUserWorkItem(delegate { using (FileStream s = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) using (BinaryWriter bw = new BinaryWriter(s)) { bw.Write(new[] { (byte)'f', (byte)'V', (byte)'i', (byte)'s' }); const int flags = 0; bw.Write(flags); string description = selected.Text; bw.Write(description); // Save graph viewport settings long axisX = graph.AxisX; long axisY = graph.AxisY; double scaleFactor = graph.ScaleFactor; bw.Write(axisX); bw.Write(axisY); bw.Write(scaleFactor); if (ae.VariableName == null) { // Constant function, save only one value double y = ae.Evaluate(double.NaN); const long count = 2; bw.Write(count); bw.Write(double.MinValue); bw.Write(y); bw.Write(double.MaxValue); bw.Write(y); } else { bw.Write(distance + 1); Stopwatch sw = Stopwatch.StartNew(); long lastProcessed = xFi; for (long xi = xFi; xi <= xLi; xi++) { double x = *(double *)ξ double y = ae.Evaluate(x); bw.Write(x); bw.Write(y); if (sw.ElapsedMilliseconds > 4000) { if (progressDialog.IsCancelled) { break; } sw.Stop(); long delta = (xi - lastProcessed); long rate = delta * 1000 / (long)sw.Elapsed.TotalMilliseconds; TimeSpan remaining = TimeSpan.FromSeconds((xLi - xi) / rate); lastProcessed = xi; BeginInvoke((MethodInvoker) delegate { progressDialog.Line1 = Tx.T("dataset.saving.progress", Tx.N(lastProcessed - xFi), Tx.N(xLi - xFi)); progressDialog.Line2 = Tx.T("main.remaining time", remaining.ToTextString()); progressDialog.Progress = (int)((lastProcessed - xFi) * 100 / (xLi - xFi)); }); sw.Restart(); } } sw.Stop(); } } if (progressDialog.IsCancelled) { try { File.Delete(filename); } catch { // Nothing to do... } } BeginInvoke((MethodInvoker) delegate { progressDialog.TaskCompleted(); }); }, null); } }