//-----------------------------------------------------------------------
//
// Copyright © 2012 Nils Hammar. All rights reserved.
//
//-----------------------------------------------------------------------
/*
* Software to access vehicle information via the OBD-II connector.
*
* Copyright © 2012 Nils Hammar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Alternative licensing is possible, see the licensing document.
*
* The above text may not be removed or modified.
*/
namespace UserInterface.GUI.OBD
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using global::Protocol;
using global::Protocol.OBD;
using global::SharedObjects;
using global::SharedObjects.DataMgmt;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Protocol;
///
/// Class for OBD data selection.
///
public partial class ObdDataPanel : AbstractUserControl, IObdDataPanel
{
///
/// Position of ECU info column.
///
private int ecuInfoColumnPos = 0;
///
/// Data source instance.
///
private IDataSource iDataSource;
///
/// Indicate if FreezeFrame panel or Normal panel.
///
private uint expectedMode;
///
/// List of source addresses (ECUs)
///
private List sourceAddresses = new List();
///
/// Columns in the data grid view.
///
private DataGridViewColumnCollection cols;
///
/// Rows in the data grid view.
///
private DataGridViewRowCollection rows;
///
/// Mode directory of PID groups.
///
private SortedDictionary> modeData = new SortedDictionary>();
///
/// Array of known ECUs for vehicle.
///
private List ecus;
///
/// Icon indicating plot is possible.
///
private Image iconPlot;
///
/// Icon indicating plot is not possible.
///
private Image iconNoplot;
///
/// Data file directory path.
///
private string dataFileDir;
///
/// Messaging interface instance.
///
private IMessaging iMessaging;
///
/// Known ECU addresses.
///
private Dictionary ecuAddressList = new Dictionary();
///
/// Row that was last clicked.
///
private int clickRow = -1;
///
/// Column that was last clicked.
///
private int clickCol = -1;
///
/// Initializes a new instance of the class.
///
public ObdDataPanel()
{
this.InitializeComponent();
this.cols = this.dataGridView1.Columns;
this.rows = this.dataGridView1.Rows;
this.dataGridView1.SortCompare += new DataGridViewSortCompareEventHandler(this.dataGridView1_SortCompare);
this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
}
///
/// Initializes the panel.
///
/// Logging instance.
/// Messaging instance.
/// Application tree instance.
/// Expected Mode byte value to handle.
/// Data source interface instance.
/// Vehicle instance.
/// Data file directory path.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "5", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iMessaging", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "expectedMode", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dataFileDir", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iLogging", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iDataSource", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "freezeFrame", Justification = "Reviewed, intentional.")]
public void init(
ILogging iLogging,
IMessaging iMessaging,
IApplicationTree applicationTree,
uint expectedMode,
IDataSource iDataSource,
XmlClass.vehicle vehicle,
string dataFileDir)
{
this.iLogging = iLogging;
this.iMessaging = iMessaging;
this.expectedMode = expectedMode;
this.iDataSource = iDataSource;
this.ecus = vehicle.ecus;
this.dataFileDir = dataFileDir;
this.setApplicationTree(applicationTree);
this.iconPlot = this.getImage("chart_curve.png");
this.iconNoplot = new Bitmap(16, 16);
this.refreshModeData(vehicle);
this.col_plotgroup.Items.Add("-");
this.col_plotgroup.Items.Add("1");
this.col_plotgroup.Items.Add("2");
this.col_plotgroup.Items.Add("3");
this.col_plotgroup.Items.Add("4");
this.col_plotgroup.Items.Add("5");
this.col_plotgroup.Items.Add("6");
this.col_plotgroup.Items.Add("7");
this.col_plotgroup.Items.Add("8");
this.col_plotgroup.Items.Add("9");
}
///
/// Refresh the ModeData dictionary.
///
/// Current vehicle instance.
public void refreshModeData(XmlClass.vehicle vehicle)
{
if (vehicle != null)
{
this.modeData = new SortedDictionary>();
foreach (XmlClass.vehicle.pidgroup vehiclePidGroup in vehicle.pidgroups)
{
foreach (XmlClass.pidgroup pidGroup in this.iDataSource.restrictedPidgroups)
{
if (pidGroup.id == vehiclePidGroup.id && !pidGroup.hasMap)
{
SortedDictionary pidList = this.getPidList(pidGroup.mode_int);
foreach (XmlClass.pidgroup.pidlist pid in pidGroup.pids)
{
if (!pidList.ContainsKey(pid.pid_int))
{
PidItemWrapper pidItemWrapper = new PidItemWrapper(pidGroup.mode_int, pidGroup.mode_int, pid, true);
pidList.Add(pid.pid_int, pidItemWrapper);
}
}
}
}
}
}
}
///
/// Clear the current table.
///
public void clear()
{
this.rows.Clear();
}
///
/// Add one data item.
///
/// Source address.
/// Mode byte value.
/// PID value.
/// Bitmap mask for active items.
public void addDataItem(uint srcAddress, byte mode0, uint pid, int bitmap)
{
byte mode = (byte)(mode0 & 0xbf);
if (mode == this.expectedMode)
{
byte searchMode = mode;
if (mode == 0x02)
{
searchMode = 0x01;
}
if (!this.sourceAddresses.Contains(srcAddress))
{
this.addColumn(srcAddress);
this.ecuInfoColumnPos++;
this.sourceAddresses.Add(srcAddress);
}
int srcAddressIndex = this.sourceAddresses.IndexOf(srcAddress);
SortedDictionary pidList = this.getPidList(mode);
for (int i = 0; i < 31; i++)
{
bool enable = (((bitmap >> (31 - i)) & 0x01) == 1);
if (enable)
{
uint pidNum = (uint)(pid + i + 1);
XmlClass.pidgroup.pidlist item = this.findPidItem(searchMode, pidNum);
if (item != null)
{
PidItemWrapper pidItemWrapper;
if (pidList.ContainsKey(item.pid_int))
{
pidItemWrapper = pidList[item.pid_int];
}
else
{
pidItemWrapper = new PidItemWrapper(mode, searchMode, item, false);
pidList.Add(item.pid_int, pidItemWrapper);
}
if (!pidItemWrapper.checkedColumns.Contains(srcAddressIndex))
{
pidItemWrapper.checkedColumns.Add(srcAddressIndex);
}
}
}
}
}
}
///
/// Display the data fields.
///
public void displayFields()
{
if (this.dataGridView1.InvokeRequired)
{
try
{
this.Invoke(new displayFieldsFunc(this.displayFields), new object[] { });
}
catch (ThreadAbortException)
{
throw;
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
this.rows.Clear();
foreach (KeyValuePair> kvp in this.modeData)
{
foreach (KeyValuePair kvp2 in kvp.Value)
{
PidItemWrapper pidItemWrapper = kvp2.Value;
int rowNum = this.rows.Add();
DataGridViewRow row = this.rows[rowNum];
DataGridViewCellCollection cells = row.Cells;
cells["col_item"].Value = pidItemWrapper;
cells["col_mode"].Value = "0x" + kvp.Key.ToString("x2");
cells["col_pid"].Value = "0x" + pidItemWrapper.pidItem.pid_int.ToString("x2");
cells["col_freq"].Value = string.IsNullOrEmpty(pidItemWrapper.pidItem.refresh) ? "1:1" : pidItemWrapper.pidItem.refresh;
cells["col_description"].Value = pidItemWrapper.pidItem.name;
cells["col_plottable"].Value = pidItemWrapper.pidItem.canPlot ? this.iconPlot : this.iconNoplot;
for (int i = 0; i < this.ecuInfoColumnPos; i++)
{
bool flg = pidItemWrapper.checkedColumns.Contains(i);
cells["ecu_" + i].ReadOnly = !(flg || pidItemWrapper.defaultChecked);
if (!cells["ecu_" + i].ReadOnly)
{
cells["ecu_" + i].ContextMenuStrip = this.contextMenuStrip1;
}
if (pidItemWrapper.defaultChecked)
{
cells["ecu_" + i].Style.BackColor = Color.Yellow;
}
else
{
cells["ecu_" + i].Style.BackColor = flg ? Color.LightGreen : Color.Gray;
}
}
cells["col_plotgroup"].Value = "-";
}
}
}
}
///
/// Get list of selected items.
///
/// List of selected items.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Reviewed.")]
public IList getSelectedItems()
{
List items = new List();
foreach (DataGridViewRow row in this.rows)
{
DataGridViewCellCollection cells = row.Cells;
if (cells != null)
{
List addrList = new List();
foreach (int ecuCol in this.ecuAddressList.Keys)
{
if (!cells["ecu_" + ecuCol].ReadOnly)
{
if (cells["ecu_" + ecuCol] != null
&& cells["ecu_" + ecuCol].Value != null
&& (bool)cells["ecu_" + ecuCol].Value)
{
addrList.Add(this.ecuAddressList[ecuCol]);
}
}
}
if (addrList.Count > 0)
{
string plotgroup = (string)cells["col_plotgroup"].Value;
int plotgroupInt = 0;
try
{
plotgroupInt = Convert.ToInt32(plotgroup);
}
catch
{
}
PidItemWrapper pidItemWrapper = (PidItemWrapper)cells["col_item"].Value;
pidItemWrapper.addrList = addrList;
pidItemWrapper.plotGroup = plotgroupInt;
items.Add(pidItemWrapper);
}
}
}
return items;
}
///
/// Restore saved data.
///
/// Items to be checked at restore.
public void selectItems(IList restoredItems)
{
if (restoredItems != null)
{
Dictionary colMapper = new Dictionary();
foreach (KeyValuePair kvp in this.ecuAddressList)
{
colMapper.Add(kvp.Value, kvp.Key);
}
foreach (DataGridViewRow row in this.rows)
{
DataGridViewCellCollection cells = row.Cells;
cells["col_plotgroup"].Value = "-";
foreach (int col in this.ecuAddressList.Keys)
{
cells["ecu_" + col].Value = false;
}
PidItemWrapper pidItemWrapper = (PidItemWrapper)cells["col_item"].Value;
foreach (XmlClass.selectionModePid item in restoredItems)
{
if (pidItemWrapper.pidMode == item.mode_int
&& pidItemWrapper.pidItem.pid_int == item.pid_int)
{
foreach (XmlClass.selectionModePid.addressItem addr in item.addrList)
{
if (colMapper.ContainsKey(addr.address))
{
int col = colMapper[addr.address];
if (item.plotGroup > 0)
{
cells["col_plotgroup"].Value = item.plotGroup.ToString();
}
cells["ecu_" + col].Value = true;
}
}
}
}
}
}
}
///
/// Construct a new column.
///
/// Column header text.
/// Name of column.
/// DataGridViewColumn instance.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed, intentional.")]
private static DataGridViewColumn getColumn(string header, string name)
{
DataGridViewCellStyle style = new DataGridViewCellStyle();
style.Alignment = DataGridViewContentAlignment.MiddleCenter;
style.BackColor = Color.LightGray;
DataGridViewColumn newCol = new System.Windows.Forms.DataGridViewCheckBoxColumn();
newCol.Width = 60;
newCol.HeaderText = header;
newCol.ReadOnly = true;
newCol.Name = name;
newCol.Visible = true;
newCol.SortMode = DataGridViewColumnSortMode.Automatic;
return newCol;
}
///
/// Get one image.
///
/// Name of icon file.
/// Image loaded from file.
private Image getImage(string filename)
{
Image img = Image.FromFile(this.dataFileDir + "Icons\\" + filename);
return img;
}
///
/// Display the data fields.
///
private delegate void displayFieldsFunc();
///
/// Add one column for an ECU.
///
/// ECU address.
private delegate void addColumnFunc(uint srcAddress);
///
/// Add one column for an ECU.
///
/// ECU address.
private void addColumn(uint srcAddress)
{
if (this.dataGridView1.InvokeRequired)
{
try
{
this.Invoke(new addColumnFunc(this.addColumn), new object[] { srcAddress });
}
catch (ThreadAbortException)
{
throw;
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
string ecuName = "ECU";
string addressStr = string.Empty;
uint mask;
if (srcAddress < 0x800 && srcAddress > 0xff)
{
mask = 0x07;
addressStr = srcAddress.ToString("x4");
}
else
{
mask = 0xff;
addressStr = ((srcAddress >> 16) & 0xffff).ToString("x4") + " " + (srcAddress & 0xffff).ToString("x4");
}
foreach (XmlClass.vehicle.ecuData ecu in this.ecus)
{
uint id = Utils.hexParse(ecu.id);
if ((id & mask) == (srcAddress & mask))
{
ecuName = ecu.name;
break;
}
}
uint? destinationAddress = DataRequester.getDestinationAddress(this.iMessaging.protocolHandler, srcAddress);
if (destinationAddress != null)
{
this.ecuAddressList.Add(this.ecuInfoColumnPos, (uint)destinationAddress);
}
this.cols.Insert(5 + this.ecuInfoColumnPos, getColumn(ecuName + " 0x" + addressStr, "ecu_" + this.ecuInfoColumnPos));
this.calcColumnSize();
}
}
///
/// Get the PID list for the given mode.
///
/// Mode to get list for.
/// PID list.
private SortedDictionary getPidList(uint mode)
{
SortedDictionary pidList;
if (this.modeData.ContainsKey(mode))
{
pidList = this.modeData[mode];
}
else
{
pidList = new SortedDictionary();
this.modeData.Add(mode, pidList);
}
return pidList;
}
///
/// Find item in pid group list.
///
/// Mode for item.
/// PID number for item.
/// PID Item.
private XmlClass.pidgroup.pidlist findPidItem(byte mode, uint pidNum)
{
XmlClass.pidgroup.pidlist item = null;
foreach (XmlClass.pidgroup pidgroup in this.iDataSource.restrictedPidgroups)
{
if (pidgroup.mode_int == (uint)mode)
{
foreach (XmlClass.pidgroup.pidlist pidItem in pidgroup.pids)
{
if (pidItem.pid_int == pidNum)
{
item = pidItem;
break;
}
}
if (item != null)
{
break;
}
}
}
return item;
}
///
/// Handle change of display size.
///
/// Sending object.
/// Event data.
private void dataGridView1_SizeChanged(object sender, EventArgs e)
{
this.calcColumnSize();
}
///
/// Recalculate the column size for the Description field.
///
private void calcColumnSize()
{
int w0 = this.dataGridView1.Width - 60;
foreach (DataGridViewColumn column in this.cols)
{
if (column.Visible)
{
w0 -= column.Width;
}
}
w0 += this.cols["col_description"].Width;
if (w0 < 100)
{
w0 = 100;
}
this.cols["col_description"].Width = w0;
}
///
/// Handle data error event.
///
/// Sending object.
/// Event data.
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
if (MessageBox.Show(
"col=" + e.ColumnIndex + ", row=" + e.RowIndex + "\r\n" + e.Exception.Message + "\r\n" + "Context=" + e.Context + "\r\n" + e.Exception.StackTrace,
"Error",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Stop) == DialogResult.Cancel)
{
e.ThrowException = true;
}
if (e.Exception.InnerException != null)
{
MessageBox.Show(
"InnerException=" + e.Exception.InnerException.Message + "\r\n" + e.Exception.InnerException.StackTrace,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
e.Cancel = true;
}
///
/// Compare two rows for sorting on image.
///
/// Sending object.
/// Event data.
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
try
{
switch (e.Column.Name)
{
case "col_mode":
this.modeColumnComparer(e);
break;
case "col_plottable":
this.plottableColumnComparer(e);
break;
default:
if (e.Column.Name.StartsWith("ecu_"))
{
this.ecuColumnComparer(e);
}
break;
}
}
catch (Exception ex)
{
this.iLogging.appendText(ex.GetType().ToString() + ": " + ex.Message + "\r\n" + ex.StackTrace + "\r\n");
}
}
///
/// Compare ECU column rows.
///
/// Event data.
private void ecuColumnComparer(DataGridViewSortCompareEventArgs e)
{
int res = 0;
int col = e.Column.Index;
int row1 = e.RowIndex1;
int row2 = e.RowIndex2;
if (!this.dataGridView1[col, row1].ReadOnly && this.dataGridView1[col, row2].ReadOnly)
{
res = -1;
}
else
{
if (this.dataGridView1[col, row1].ReadOnly && !this.dataGridView1[col, row2].ReadOnly)
{
res = 1;
}
else
{
if (e.CellValue1 != null && e.CellValue2 != null)
{
if (((bool)e.CellValue1) && !((bool)e.CellValue2))
{
res = -1;
}
else
{
if (!((bool)e.CellValue1) && ((bool)e.CellValue2))
{
res = 1;
}
}
}
else
{
if (e.CellValue1 != null && e.CellValue2 == null)
{
res = -1;
}
else
{
if (e.CellValue1 == null && e.CellValue2 != null)
{
res = 1;
}
}
}
}
}
// If equal then sort by mode&pid.
if (res == 0)
{
res = this.modeColumnComparer(e);
}
e.SortResult = res;
e.Handled = true;
}
///
/// Compare for the plottable column.
/// This compares images by comparing their hashcode since
/// we are just interested in getting them grouped.
///
/// Event data.
private void plottableColumnComparer(DataGridViewSortCompareEventArgs e)
{
int res = 0;
if (e.CellValue1 != null && e.CellValue2 != null)
{
int h1 = e.CellValue1.GetHashCode();
int h2 = e.CellValue2.GetHashCode();
if (h1 > h2)
{
res = 1;
}
else
{
if (h1 < h2)
{
res = -1;
}
}
}
else
{
if (e.CellValue1 != null && e.CellValue2 == null)
{
res = 1;
}
else
{
if (e.CellValue1 == null && e.CellValue2 != null)
{
res = -1;
}
}
}
// If equal then sort by mode&pid.
if (res == 0)
{
res = this.modeColumnComparer(e);
}
e.SortResult = res;
e.Handled = true;
}
///
/// Compare Mode and PID columns to sort first on Mode and then on PID.
///
/// Event data.
/// Result of compare.
private int modeColumnComparer(DataGridViewSortCompareEventArgs e)
{
int res = 0;
DataGridViewRow row1 = this.dataGridView1.Rows[e.RowIndex1];
DataGridViewRow row2 = this.dataGridView1.Rows[e.RowIndex2];
string s1 = (string)row1.Cells["col_mode"].Value;
string s2 = (string)row2.Cells["col_mode"].Value;
uint i1 = Utils.hexParse(s1);
uint i2 = Utils.hexParse(s2);
res = ((i1 == i2) ? 0 : ((i1 > i2) ? 1 : -1));
if (res == 0)
{
string s1a = (string)row1.Cells["col_pid"].Value;
string s2a = (string)row2.Cells["col_pid"].Value;
uint i1a = Utils.hexParse(s1a);
uint i2a = Utils.hexParse(s2a);
res = ((i1a == i2a) ? 0 : ((i1a > i2a) ? 1 : -1));
}
e.SortResult = res;
e.Handled = true;
return res;
}
///
/// Hide cells with readonly checkboxes for ECUs.
///
/// Sending object.
/// Event data.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex >= 0 && e.RowIndex >= 0)
{
DataGridView dataGridView = (DataGridView)sender;
DataGridViewColumn dataGridViewColumn = dataGridView.Columns[e.ColumnIndex];
if (dataGridViewColumn.Name.StartsWith("ecu_"))
{
DataGridViewCell dataGridViewCell = dataGridView[e.ColumnIndex, e.RowIndex];
if (dataGridViewCell.ReadOnly)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
}
}
///
/// Select/unselect all columns from current column and onwards.
///
/// Sending object.
/// Event data.
private void selectRightToolStripMenuItem_Click(object sender, EventArgs e)
{
DataGridViewCell dataGridViewCell = this.dataGridView1[this.clickCol, this.clickRow];
if (!dataGridViewCell.ReadOnly)
{
bool chkVal = dataGridViewCell.Value != null ? (bool)dataGridViewCell.Value : false;
bool newVal = !chkVal;
foreach (int ecuCol in this.ecuAddressList.Keys)
{
int i = ecuCol + 5;
if (i >= this.clickCol)
{
if (!this.dataGridView1[i, this.clickRow].ReadOnly)
{
this.dataGridView1[i, this.clickRow].Value = newVal;
}
}
}
}
}
///
/// Select/unselect all rows from current row and down of the same type for the given column.
///
/// Sending object.
/// Event data.
private void selectColumnDownToolStripMenuItem_Click(object sender, EventArgs e)
{
DataGridViewCell dataGridViewCell = this.dataGridView1[this.clickCol, this.clickRow];
object plotVal = this.dataGridView1.Rows[this.clickRow].Cells["col_plottable"].Value;
bool plottable = (plotVal != null && this.iconPlot.Equals(plotVal));
if (!dataGridViewCell.ReadOnly)
{
bool chkVal = dataGridViewCell.Value != null ? (bool)dataGridViewCell.Value : false;
bool newVal = !chkVal;
int n = this.dataGridView1.Rows.Count;
for (int i = this.clickRow; i < n; i++)
{
object plotVal1 = this.dataGridView1.Rows[i].Cells["col_plottable"].Value;
bool plottable1 = (plotVal1 != null && this.iconPlot.Equals(plotVal1));
if (!this.dataGridView1[this.clickCol, i].ReadOnly && plottable1 == plottable)
{
this.dataGridView1[this.clickCol, i].Value = newVal;
}
}
}
}
///
/// Save last cell where a mouse click occurred.
///
/// This is used for the context menu actions.
///
///
/// Sending object.
/// Event data.
private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
this.clickRow = e.RowIndex;
this.clickCol = e.ColumnIndex;
}
}
}