using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Xml; using System.Windows.Forms; using TradeIdeas.TIQData; using TradeIdeas.ServerConnection; using TradeIdeas.XML; using TradeIdeas.MiscSupport; namespace TradeIdeas.TIQ { public partial class PrototypeEditor : UserControl { private int _rowTally = 0; //used to keep current number of rows in datagrid private int _rowIndex = 0; //used for delete function private int _columnIndex = 0; //used for delete function private int _columnDisplayIndex = 0; private List rowList = new List(); //for the copy/paste private List colList = new List(); //for copy/paste private DataGridView.HitTestInfo _hit; //for determining visibility of certain context menu items public static string _pasteText; //for pasting copied grid items private static bool _pastingPrototype = false; private const int TRADING_DAY = 390; //if there's data on the chart(_dataList > 0 --see SeriesEditor) and someone //tries do stuff like add/remove column, rename column..an exception gets thrown. //This bool is to temporarly turn off all of those computation that happen within UpdateChart(series editor method) //when the operation is finished, boolean gets set back to false.(user should then request data again to get updated chart) public static bool makingNonDataProcessingChange = false; public bool ClickingFromCsvPreview { get; set; } public bool Intraday { get { return rdoIntraday.Checked; } } public XmlNode SetContents(XmlNode destination) //akin to "Save Layout" of TIPro { XmlNode prototype = destination.NewNode("PROTOTYPE"); XmlNode grid = prototype.NewNode("GRID"); XmlNode rows = grid.NewNode("ROWS"); XmlNode columns = grid.NewNode("COLUMNS"); //Saving checkbox status: prototype.SetProperty("INITIAL_TIME_CHECKED", chkInitialTime.Checked.ToString()); prototype.SetProperty("INITIAL_COUNT_CHECKED", chkInitialCount.Checked.ToString()); prototype.SetProperty("REMOVE_BLANKS_CHECKED", chkRemoveBlanks.Checked.ToString()); DataGridViewSelectedCellCollection d = dataGridView1.SelectedCells; if (d.Count == 1 ) //if there is a single selected cell-we must note. (determines what goes into the big text area...) { foreach (DataGridViewCell c in d) { int dIndex = c.OwningColumn.DisplayIndex; //the columns may have been rearranged... prototype.SetProperty("SELECTED_CELL_ROW_INDEX", c.RowIndex); prototype.SetProperty("SELECTED_CELL_COLUMN_INDEX", dIndex); } } //Saving textbox items (i.e. initial count) prototype.SetProperty("INITIAL_COUNT_TEXTBOX", txtInitialCount.Text); //Save initial time TimeSpan total = initialTimeDateTimePicker.Value.TimeOfDay; long timeSeconds = (long)total.TotalSeconds; prototype.SetProperty("INITIAL_TIME", timeSeconds.ToString()); //Save initial date prototype.SetProperty("INITIAL_DATE", ServerFormats.ToTimeT(initialTimeDateTimePicker.Value.Date)); //SAVE THE ITEMS IN THE DATAGRID: if (ClickingFromCsvPreview) { columns.SetProperty("NUMBER_OF_COLUMNS", 5); //This is where we're gathering info as a result from clicking // on a cell within a Csv preview (e.g. the EoCAlertHistory). //In this case we only want two rows, that for the open,high,low,close,volume //and the second which contains their respective formulas XmlNode rw0 = rows.NewNode("ROW"); XmlNode cell1 = rw0.NewNode("CELL"); cell1.SetProperty("CELL_CONTENTS", "OPEN"); XmlNode cell2 = rw0.NewNode("CELL"); cell2.SetProperty("CELL_CONTENTS", "HIGH"); XmlNode cell3 = rw0.NewNode("CELL"); cell3.SetProperty("CELL_CONTENTS", "LOW"); XmlNode cell4 = rw0.NewNode("CELL"); cell4.SetProperty("CELL_CONTENTS", "CLOSE"); XmlNode cell5 = rw0.NewNode("CELL"); cell5.SetProperty("CELL_CONTENTS", "VOLUME"); XmlNode rw1 = rows.NewNode("ROW"); XmlNode cell6 = rw1.NewNode("CELL"); cell6.SetProperty("CELL_CONTENTS", "current(\"open\")"); XmlNode cell7 = rw1.NewNode("CELL"); cell7.SetProperty("CELL_CONTENTS", "current(\"high\")"); XmlNode cell8 = rw1.NewNode("CELL"); cell8.SetProperty("CELL_CONTENTS", "current(\"low\")"); XmlNode cell9 = rw1.NewNode("CELL"); cell9.SetProperty("CELL_CONTENTS", "current(\"close\")"); XmlNode cell10 = rw1.NewNode("CELL"); cell10.SetProperty("CELL_CONTENTS", "current(\"volume\")"); } else { columns.SetProperty("NUMBER_OF_COLUMNS", dataGridView1.Columns.Count); //Now save the rows of the datagrid foreach (DataGridViewRow r in dataGridView1.Rows) { SortedList arrangedCells = orderTheRowCells(r); //experimental XmlNode rw = rows.NewNode("ROW"); foreach (var pair in arrangedCells) //iteration goes from 0th column to nth column { if (pair.Value == null) //This would be an empty cell-avoid a null exception. { XmlNode temp = rw.NewNode("CELL"); temp.SetProperty("CELL_CONTENTS", ""); continue; } XmlNode cell = rw.NewNode("CELL"); if ((pair.Value).Value == null) { (pair.Value).Value = ""; } cell.SetProperty("CELL_CONTENTS", (pair.Value).Value.ToString()); } } } //set property of the daily/intraday radio buttons: prototype.SetProperty("RADIO_DAILY", rdoDaily.Checked); prototype.SetProperty("RADIO_INTRADAY", rdoIntraday.Checked); //set the property of the intraday panel and the items within: prototype.SetProperty("MINUTES_PER_CANDLE", minutesPerCandleNumericUpDown.Value); prototype.SetProperty("START_EARLY", startEarlyNumericUpDown.Value); prototype.SetProperty("END_LATE", endLateNumericUpDown.Value); prototype.SetProperty("FIRST_CANDLE", chkFirstCandleShorter.Checked); return destination; } public void GetContents(XmlNode parent) //akin to "Restore Layout" of TIPro { XmlNode prototype = parent.Node("PROTOTYPE"); //Restoring the settings on the form... chkInitialTime.Checked = prototype.Property("INITIAL_TIME_CHECKED", false); chkInitialCount.Checked = prototype.Property("INITIAL_COUNT_CHECKED", true); chkRemoveBlanks.Checked = prototype.Property("REMOVE_BLANKS_CHECKED", false); txtInitialCount.Text = prototype.Property("INITIAL_COUNT_TEXTBOX", ""); //restore the initial time and date; long time = 0; if (prototype.Property("INITIAL_DATE") == "") { time = Convert.ToInt64(prototype.Property("INITIAL_TIME")); } else { long timeSeconds = Convert.ToInt64(prototype.Property("INITIAL_TIME")); long dateSeconds = (Convert.ToInt64(prototype.Property("INITIAL_DATE"))); time = timeSeconds + dateSeconds; } DateTime value; DateTime? dT = ServerFormats.FromTimeT(time); if (dT == null) { value = DateTime.Now; } else { value = (DateTime)dT; } initialTimeDateTimePicker.Value = value; //now restore the grid XmlNode columns = prototype.Node("GRID").Node("COLUMNS"); XmlNode rows = prototype.Node("GRID").Node("ROWS"); dataGridView1.Columns.Clear(); //creating the correct number of columns in grid int columnCount = Convert.ToInt32(columns.Property("NUMBER_OF_COLUMNS",0)); for (int i = 0; i < columnCount; i++) { dataGridView1.Columns.Add("", ""); } //create the correct number of rows in grid foreach (XmlNode row in rows.Enum()) { dataGridView1.Rows.Add(); int count = dataGridView1.Rows.Count; int tally = 0; foreach (XmlNode cell in row.Enum()) //the number of cell in a row = number of columns in grid { //When a row is added to datagrid-it gets populated with the cell contents.. dataGridView1.Rows[count - 1].Cells[tally].Value = cell.Property("CELL_CONTENTS",""); tally++; } } int selectedRowIndex = prototype.Property("SELECTED_CELL_ROW_INDEX", -1); int selectedColumnIndex = prototype.Property("SELECTED_CELL_COLUMN_INDEX", -1); if (selectedColumnIndex != -1 && selectedRowIndex != -1) //set selected cell (if any) { dataGridView1.CurrentCell = dataGridView1[selectedColumnIndex, selectedRowIndex]; } //restore the intraday panel items: minutesPerCandleNumericUpDown.Value = prototype.Property("MINUTES_PER_CANDLE", 1); startEarlyNumericUpDown.Value = prototype.Property("START_EARLY", 0); endLateNumericUpDown.Value = prototype.Property("END_LATE", 0); chkFirstCandleShorter.Checked = prototype.Property("FIRST_CANDLE", false); //restore the Daily/Intraday radio button status; rdoDaily.Checked = prototype.Property("RADIO_DAILY", true); rdoIntraday.Checked = prototype.Property("RADIO_INTRADAY", false); if (dataGridView1.CurrentCell == null) { cellEditorTextBox.Text = ""; } else { cellEditorTextBox.Text = dataGridView1.CurrentCell.Value.ToString(); } } public PrototypeEditor() { InitializeComponent(); dataGridView1.Rows.Add(); dataGridView1.Rows.Add(); CheckCount(); upDateRows(); checkDailyIntraday(); enableFirstCandleShorter(); ClickingFromCsvPreview = false; //enable ability to copy contents of datagrid to clipboard dataGridView1.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText; /*The default initial column in grid shown when window is first instantiated is sortable. We wish to disable this.*/ dataGridView1.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable; } private void addColumnButton_Click(object sender, EventArgs e) { /*When a new column is added, we wish to highlight(select) the topmost cell of that new column*/ makingNonDataProcessingChange = true; dataGridView1.Columns.Add("", ""); dataGridView1.ClearSelection(); dataGridView1[dataGridView1.Columns.Count - 1, 0].Selected = true; UpdateCellEditor(); makingNonDataProcessingChange = false; } private void addRowButton_Click(object sender, EventArgs e) { dataGridView1.Rows.Add(new DataGridViewRow()); upDateRows(); } private void UpdateCellEditor() { if (dataGridView1.SelectedCells.Count != 1) { cellEditorTextBox.Text = ""; cellEditorTextBox.Enabled = false; } else { DataGridViewCell cell = dataGridView1.SelectedCells[0]; object value = cell.Value ?? ""; cellEditorTextBox.Text = value.ToString(); cellEditorTextBox.Enabled = true; cellEditorTextBox.Focus(); cellEditorTextBox.SelectAll(); } } private void dataGridView1_SelectionChanged(object sender, EventArgs e) { /*Each time someone selects cells, this event handler fires. So, when one selects multiple (assume contiguous) cells for the copy/paste, we need to know the "coordinates" of said cells before we do the copy/paste operation. I take the minimum value of both both the row and column lists and set this to be the initializing value of the respective "row" and "column" variable in the paste method. this is to fix a bug that was happening with the paste*/ rowList.Clear(); colList.Clear(); foreach (DataGridViewCell c in dataGridView1.SelectedCells) { int r = c.RowIndex; int col = c.ColumnIndex; rowList.Add(r); colList.Add(col); } UpdateCellEditor(); } private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { UpdateCellEditor(); } private void cellEditorTextBox_TextChanged(object sender, EventArgs e) { if (dataGridView1.SelectedCells.Count == 1) { makingNonDataProcessingChange = true; DataGridViewCell cell = dataGridView1.SelectedCells[0]; cell.Value = cellEditorTextBox.Text.Trim(); makingNonDataProcessingChange = false; } } private void initialCountCheckBox_CheckedChanged(object sender, EventArgs e) { if (!chkInitialCount.Checked) chkInitialTime.Checked = true; CheckCount(); } private void initialTimeCheckBox_CheckedChanged(object sender, EventArgs e) { if (!chkInitialTime.Checked) chkInitialCount.Checked = true; CheckCount(); } private void dataGridView1_ColumnAdded(object sender, DataGridViewColumnEventArgs e) { /*We don't want any columns in the grid to be sortable*/ dataGridView1.Columns[e.Column.Index].SortMode = DataGridViewColumnSortMode.NotSortable; InvokeColumnHeadingsChanged(); } public string GetTcl() { StringBuilder grid = new StringBuilder(); foreach (DataGridViewColumn col in GetColumnsInOrder()) { StringBuilder column = new StringBuilder(); StringBuilder formulas = new StringBuilder(); bool first = true; foreach (DataGridViewRow row in dataGridView1.Rows) { DataGridViewCell cell = row.Cells[col.Index]; if (first) { // This is the column heading. column.LAppend((cell.Value ?? "").ToString()); first = false; } else { // This one of the formulas in the column. formulas.LAppend((cell.Value ?? "").ToString()); } } column.LAppend(formulas.ToString()); grid.LAppend(column.ToString()); } StringBuilder result = new StringBuilder(); result.LAppend(rdoDaily.Checked ? "daily" : "intraday"); result.LAppend(grid.ToString()); StringBuilder options = new StringBuilder(); if (chkInitialCount.Checked) { int count; if (int.TryParse(txtInitialCount.Text, out count)) { options.LAppend("row_count"); options.LAppend(count.ToString()); } } if (chkInitialTime.Checked) { options.LAppend("at_time"); options.LAppend(ServerFormats.ToTimeT(initialTimeDateTimePicker.Value).ToString()); } options.LAppend("pack"); options.LAppend(chkRemoveBlanks.Checked ? "1" : "0"); if (rdoIntraday.Checked) { options.LAppend("minutes_per_candle"); options.LAppend(minutesPerCandleNumericUpDown.Value.ToString()); options.LAppend("start_offset"); options.LAppend(startEarlyNumericUpDown.Value.ToString()); options.LAppend("end_offset"); options.LAppend(endLateNumericUpDown.Value.ToString()); if (chkFirstCandleShorter.Enabled) { options.LAppend("first_candle_size"); if (chkFirstCandleShorter.Checked) // It won't really be 0 minutes. The server will see that this is smaller than // the smallest legal value, so it will take the smallest legal value. options.LAppend("0"); else options.LAppend(minutesPerCandleNumericUpDown.Value.ToString()); } } result.LAppend(options.ToString()); return result.ToString(); } private SortedList orderTheRowCells(DataGridViewRow row) { //Here we're rearranging the cells based upon the *Display* index of their corresponding column SortedList sortedCells = new SortedList(); foreach (DataGridViewCell cell in row.Cells) { DataGridViewColumn col = cell.OwningColumn; sortedCells.Add(col.DisplayIndex, cell); } return sortedCells; } private IList GetColumnsInOrder() { SortedList sortedColumns = new SortedList(); foreach (DataGridViewColumn col in dataGridView1.Columns) { sortedColumns.Add(col.DisplayIndex, col); } return sortedColumns.Values; } private void initialCountTextBox_TextChanged(object sender, EventArgs e) { CheckCount(); } private void CheckCount() { if ((txtInitialCount.Text == "") && !chkInitialCount.Checked) { txtInitialCount.BackColor = SystemColors.Window; return; } int unused; if (int.TryParse(txtInitialCount.Text, out unused)) txtInitialCount.BackColor = SystemColors.Window; else txtInitialCount.BackColor = Color.LightPink; } private void addRowToolStripMenuItem_Click(object sender, EventArgs e) { dataGridView1.Rows.Add(new DataGridViewRow()); upDateRows(); } private void addColumnToolStripMenuItem_Click(object sender, EventArgs e) { int index = _hit.ColumnIndex; if (index > -1) //this column index isn't necessarily the that of the *selected* cell. (It's where the user has clicked the mouse) { DataGridViewSelectedCellCollection collect = dataGridView1.SelectedCells; DataGridViewCell cell = collect[0]; int colIndex = cell.ColumnIndex; DataGridViewColumn col = new DataGridViewColumn(); col.Name = ""; col.HeaderText = ""; col.CellTemplate = new DataGridViewTextBoxCell(); makingNonDataProcessingChange = true; dataGridView1.Columns.Insert(colIndex + 1, col); makingNonDataProcessingChange = false; } } private void deleteRowToolStripMenuItem_Click(object sender, EventArgs e) { dataGridView1.Rows.RemoveAt(_rowIndex); upDateRows(); } private void deleteColumnToolStripMenuItem_Click(object sender, EventArgs e) { makingNonDataProcessingChange = true; dataGridView1.Columns.RemoveAt(_columnIndex); makingNonDataProcessingChange = false; } private void moveLeftToolStripMenuItem1_Click(object sender, EventArgs e) { DataGridViewSelectedCellCollection collect = dataGridView1.SelectedCells; List indexList = new List(); foreach(DataGridViewCell cell in collect) { indexList.Add(cell.ColumnIndex); } indexList.Sort(); List finalList = indexList.Distinct().ToList(); //remove duplicate indices SortedDictionary viewModel = canTheseColumnsBeMoved(finalList); if (viewModel != null) { //since we're shifting a column/block of contiguous columns) left, we make sure that the lowest //display index in our list is *not* zero, otherwise, the shift will not be done. if(!viewModel.ContainsKey(0)) { foreach (KeyValuePair p in viewModel) { dataGridView1.Columns[p.Value].DisplayIndex = p.Key - 1; } } } } private void moveRightToolStripMenuItem_Click(object sender, EventArgs e) { DataGridViewSelectedCellCollection collect = dataGridView1.SelectedCells; List indexList = new List(); foreach (DataGridViewCell cell in collect) { indexList.Add(cell.ColumnIndex); } indexList.Sort(); List finalList = indexList.Distinct().ToList(); //remove duplicate indices SortedDictionary viewModel = canTheseColumnsBeMoved(finalList); if (viewModel != null) { //Since we're moving right, we need the viewModel map in descending order, rather than //ascending when moving to left. IEnumerable> result = viewModel.OrderByDescending(i => i.Key); //since we're shifting a column/block of contiguous columns) right, we make sure that the highest //display index in our list is the highest index (minus one) of the datagrid, otherwise, the shift will not be done. if (!viewModel.ContainsKey(dataGridView1.Columns.Count-1)) { foreach (KeyValuePair p in result) { dataGridView1.Columns[p.Value].DisplayIndex = p.Key + 1; } } } } private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { dataGridView1.SelectAll(); } private void copyToolStripMenuItem_Click(object sender, EventArgs e) { //inspired from: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.clipboardcopymode.aspx if (dataGridView1.GetCellCount(DataGridViewElementStates.Selected) > 0) { try { // Add the selection to the clipboard. Clipboard.SetDataObject(dataGridView1.GetClipboardContent()); pastePrototypeToolStripMenuItem.Enabled = false; } catch (System.Runtime.InteropServices.ExternalException) { MessageBox.Show("The Clipboard could not be accessed. Please try again.", "Error!"); } } } private void copyPrototypeToolStripMenuItem_Click(object sender, EventArgs e) { XmlDocument doc = new XmlDocument(); doc.LoadXml(""); XmlNode node = doc.Node(0); ClickingFromCsvPreview = false; XmlNode prototype = SetContents(node); try { // Add the selection to the clipboard. // Clipboard.SetDataObject(prototype.OuterXml.ToString()); Clipboard.SetText(prototype.OuterXml.ToString()); pastePrototypeToolStripMenuItem.Enabled = true; } catch (System.Runtime.InteropServices.ExternalException) { MessageBox.Show("The Clipboard could not be accessed. Please try again.", "Error!"); } } private void pastePrototypeToolStripMenuItem_Click(object sender, EventArgs e) { try { XmlDocument doc = new XmlDocument(); doc.LoadXml(Clipboard.GetText()); XmlNode node = doc.Node(0); _pastingPrototype = true; GetContents(node); _pastingPrototype = false; InvokeColumnHeadingsChanged(true); } catch { MessageBox.Show("The Clipboard could not be accessed. Please try again.", "Error!"); } } private SortedDictionary canTheseColumnsBeMoved(List testList) { //in this method we'll take a list (containing the column indices of the selected cells) that's already been sorted (lowest to highest) //and duplictes removed. //With the list of column indices above, we now get the *display* indices SortedDictionary viewModel = new SortedDictionary(); //view (display index) is the key, the model (column index) is the value; List tempDisplay = new List(); foreach(int i in testList) { viewModel.Add(dataGridView1.Columns[i].DisplayIndex, i); tempDisplay.Add(dataGridView1.Columns[i].DisplayIndex); } tempDisplay.Sort(); //now, are these column *display* indices sequential? if not, the columns will not be moved. bool isSequential = Enumerable.Range(tempDisplay.Min(), tempDisplay.Count()).SequenceEqual(tempDisplay); //http://stackoverflow.com/questions/15914662/check-if-a-list-of-integers-increments-by-one if (isSequential) { return viewModel; } return null; } public DataGridView getDataGridView() { return dataGridView1; } private bool isClipBoardXml() { bool flag = true; try { XmlDocument doc = new XmlDocument(); doc.LoadXml(Clipboard.GetText()); flag = true; } catch { flag = false; } return flag; } private void pasteToolStripMenuItem_Click(object sender, EventArgs e) { try { /* * The code for this method was taken from the below link. However I needed to add more code to take into account * potential new column additions(depends on where user decides to paste his copied cells.) //http://codereview.stackexchange.com/questions/2042/why-is-this-code-to-paste-into-a-datagridview-so-slow */ _pasteText = Clipboard.GetText(); string s = _pasteText.Replace("\r", " "); string[] lines = s.Split('\n'); int row = rowList.Min(); int col = colList.Min(); int linesCount = lines.Count(); if ((row + linesCount) - dataGridView1.Rows.Count > 0) { dataGridView1.Rows.Add((row + linesCount) - dataGridView1.Rows.Count); upDateRows(); //update current row-count } foreach (string line in lines) { if (line.Length > 0) { string[] cells = line.Split('\t'); int tester = cells.GetLength(0); for (int i = 0; i < cells.GetLength(0); ++i) { if (col + i < dataGridView1.Columns.Count) { string val = cells[i].Trim(); dataGridView1[col + i, row].Value = val; } else if ((col + i) - dataGridView1.Columns.Count >= 0) //NEW-handles new column additions { int count = (col + i) - dataGridView1.Columns.Count; if (count == 0) { string val = cells[i].Trim(); dataGridView1.Columns.Add("", ""); dataGridView1[col + i, row].Value = val; } else { for (int j = 0; j < count; j++) { string val = cells[i].Trim(); dataGridView1.Columns.Add("", ""); dataGridView1[col + i, row].Value = val; } } } else { break; } } row++; } else { break; } } removeEmptyRow(); UpdateCellEditor(); } catch { MessageBox.Show("Error with paste operation. Please try again.", "Error!"); } } private void removeEmptyRow() { /*here we are assuming that there's an empty row that automatically forms when one does a paste from excel. One could easily say that after any paste operation: "dataGridView1.Rows.RemoveAt(dataGridView1.Rows.Count - 1)" to remove that blank row. However if one is doing a copy/paste from a single cell(not rows), a row that is not empty could get removed accidentally.-so we'll use this slighly more complex workaround where all of the cells are tested of that last row. */ bool emptyRow = true; int i = dataGridView1.Rows.Count - 1; //last row index if (dataGridView1.Rows.Count > 2) { int cellCount = dataGridView1.Rows[i].Cells.Count; //number of cells that the row has for (int k = 0; k < cellCount - 1; k++) { DataGridViewCell cell = dataGridView1[k, i]; if (cell.Value != null) { emptyRow = false; break; } } if(emptyRow) { dataGridView1.Rows.RemoveAt(i); } } } private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) { //This event handler is fired before a context-menu-item event handler... //We're determining the row and column indices to meet the requirements // for the delete row/column functionality. if (e.Button == MouseButtons.Right) { _rowIndex = e.RowIndex; upDateRows(); //double check the _rowTally when the context menu upon right mouse-click.Addressing a bug where a row that *can* be deleted, is prevented with an disabled "delete row" menu item. _columnIndex = e.ColumnIndex; DataGridViewColumn c = dataGridView1.Columns[_columnIndex]; _columnDisplayIndex = c.DisplayIndex; } } private void upDateRows() { _rowTally = dataGridView1.Rows.Count; } private void btnDaily_CheckedChanged(object sender, EventArgs e) { checkDailyIntraday(); } private void btnIntraday_CheckedChanged(object sender, EventArgs e) { checkDailyIntraday(); } private void checkDailyIntraday() { /*If the "Daily" checkbox is selected, then items within the intraday panel are disabled...If the intraday radio button is selected the panel is enabled*/ if (rdoDaily.Checked) { pnlIntraday.Enabled = false; } else { pnlIntraday.Enabled = true; } } private void startEarlyNumericUpDown_ValueChanged(object sender, EventArgs e) { enableFirstCandleShorter(); } private void endLateNumericUpDown_ValueChanged(object sender, EventArgs e) { enableFirstCandleShorter(); } private void minutesPerCandleNumericUpDown_ValueChanged(object sender, EventArgs e) { enableFirstCandleShorter(); } private void enableFirstCandleShorter() { /* normally there are 6.5 hours in the day... 390 minutes. Add start early and end late times. Is the total time in the day evenly divisible by the # of minutes / candle if it is evenly divisible, disable the "first candle..." check box. if is it not evenly divisible, enable that check box */ int dividend = TRADING_DAY + (int)endLateNumericUpDown.Value +(int) startEarlyNumericUpDown.Value; int divisor = (int)minutesPerCandleNumericUpDown.Value; if (dividend % divisor == 0) { chkFirstCandleShorter.Enabled = false; } else { chkFirstCandleShorter.Enabled = true; } } private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { //This event fires after the CellMouseDown event, and before the context menu becomes visible. /*Here, we don't want to delete the top row nor do we want to delete any row if there are only two rows remaining. We also want to have at least one column. Ergo, if there's only one column remaining, it should not be deleted*/ if (_hit.Type == DataGridViewHitTestType.Cell || _hit.Type == DataGridViewHitTestType.ColumnHeader) { if (_rowTally > 2 && _rowIndex > 0) { deleteRowToolStripMenuItem.Enabled = true; } else { deleteRowToolStripMenuItem.Enabled = false; } if (dataGridView1.Columns.Count == 1) { deleteColumnToolStripMenuItem.Enabled = false; } else { deleteColumnToolStripMenuItem.Enabled = true; } selectAllToolStripMenuItem.Enabled = true; copyToolStripMenuItem.Enabled = true; pasteToolStripMenuItem.Enabled = true; moveLeftToolStripMenuItem1.Enabled = true; moveRightToolStripMenuItem.Enabled = true; referenceThisCellToolStripMenuItem.Enabled = null != GetReferenceCode(); } else //user right-clicked on an empty area of the data grid { deleteColumnToolStripMenuItem.Enabled = false; deleteRowToolStripMenuItem.Enabled = false; selectAllToolStripMenuItem.Enabled = false; copyToolStripMenuItem.Enabled = false; pasteToolStripMenuItem.Enabled = false; moveLeftToolStripMenuItem1.Enabled = false; moveRightToolStripMenuItem.Enabled = false; referenceThisCellToolStripMenuItem.Enabled = false; } //check to see whether to enable the pasteprototype menu item if (isClipBoardXml()) { pastePrototypeToolStripMenuItem.Enabled = true; } else { pastePrototypeToolStripMenuItem.Enabled = false; } pasteFormlasFromTCLToolStripMenuItem.Enabled = false; try { string toPaste = Clipboard.GetText(); pasteFormlasFromTCLToolStripMenuItem.Enabled = ParsedColumn.IsValidGrid(toPaste); } catch { } } private void dataGridView1_MouseDown(object sender, MouseEventArgs e) { /*If someone clicks on a datacell,columnheader, or even in a 'bare area' of the datagrid, this event handler will always fire first. So, this is used as an advantage to see where the user is clicking-whether on an active portion of the datagrid, or an empty area*/ if (e.Button == MouseButtons.Right) { _hit = dataGridView1.HitTest(e.X, e.Y); } } /// /// This is a way of describing the columns. Other windows might let you select a /// column, i.e. what column to sort by. Mostly we are concerned with the name of /// the column. The name is the value stored in the top row of the column. This /// is the main way that the server talks about columns. However, each object also /// has an "identity". If a column is renamed, then we still want to sort (for /// example) by the same column. We just change the name. If a column is deleted, /// and we were sorting by a different column, we still want to sort by that /// column. If we are sorting by a column and that column is deleted, then the GUI /// will have to present another choice. /// public class ColumnHeading { private readonly DataGridViewColumn _identity; private readonly string _name; /// /// Create a new column heading. /// /// /// This is the primary way we keep up with the column, in case columns are /// renamed, rearranged, etc. This can be null. /// /// /// This is the name presented to the user. This cannot be null. /// public ColumnHeading(DataGridViewColumn identity, string name) { _identity = identity; _name = name; } /// /// This object is often used in listboxes, combo boxes, etc., so we need /// this function to return something user-friendly. /// /// The "name" provided in the constructor. public override string ToString() { return _name; } /// /// This is the primary way to talk about equality. /// /// /// The value provided for Identity in the constructor. /// This might be null. /// public object GetIdentity() { return _identity; } /// /// Compare two objects to see if they are talking about the same column. /// Some properties might be different. In particular, a column might be /// renamed. That doesn't matter. /// /// The other object to comare to. /// /// False if obj is null or a different data type. /// True if obj is the same object as this. /// True if the identities are the same and not null. /// False if either identity is null and the objects are not the same. /// i.e. if identity is not null, that's the comparison. If they are /// both null, then the object itself is its own idenity. But use /// object.Equals() to avoid an infinite recursion. /// public override bool Equals(object obj) { if (base.Equals(obj)) return true; ColumnHeading other = obj as ColumnHeading; if (null == other) return false; return _identity == other._identity; } /// /// A normal hash code. /// /// Something that works well with . public override int GetHashCode() { if (null == _identity) return base.GetHashCode(); else return _identity.GetHashCode(); } } public List GetColumnHeadings() { int count = dataGridView1.ColumnCount; List result = new List(count); for (int i = 0; i < count; i++) { if (dataGridView1.Rows.Count == 0) //sometimes Rows.Count = 0; results in bug on restoring layout return result; object value = dataGridView1.Rows[0].Cells[i].Value??""; result.Add(new ColumnHeading(dataGridView1.Columns[i], value.ToString())); } return result; } private void dataGridView1_ColumnRemoved(object sender, DataGridViewColumnEventArgs e) { InvokeColumnHeadingsChanged(); } private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex == 0) InvokeColumnHeadingsChanged(); } private void InvokeColumnHeadingsChanged(bool onlyLookAtNames=false) { if (_pastingPrototype) { return; } if (null != ColumnHeadingsChanged) ColumnHeadingsChanged(this, onlyLookAtNames); } public event Action ColumnHeadingsChanged; private String GetReferenceCode() { // _rowIndex and _columnIndex describe the cell under the mouse // when you right clicked to get the context menu. We are creating // a reference to this cell. NOTE: we must use the *displayed* column index in parts of this method. // This is because the user might rearrange the columns which will cause this method to fail // if displayed index isn't taken into account. _columnDisplayIndex if (_rowIndex == 0) // You can't reference a header cell return null; var selection = dataGridView1.SelectedCells; if (selection.Count != 1) // We're going to paste into the selected cell. // So exactly one cell must be selected. return null; int selectedRow = 0; int selectedColumn = 0; foreach (DataGridViewCell c in selection) { selectedRow = c.RowIndex; selectedColumn = c.OwningColumn.DisplayIndex; } if (selectedRow == 0) // References don't work in a row heading. return null; if (selectedRow < _rowIndex) // Can't refernce future rows. return null; if ((selectedRow == _rowIndex) && (selectedColumn <= _columnDisplayIndex)) // On the current row you can only reference cells to the left. return null; return "ref(" + (selectedRow - _rowIndex) + ",\"" + dataGridView1.Rows[0].Cells[_columnIndex].Value + "\")"; } private void referenceThisCellToolStripMenuItem_Click(object sender, EventArgs e) { String newCode = GetReferenceCode(); if (null != newCode) cellEditorTextBox.SelectedText = newCode; } private void copyPrototypeAsTCLToolStripMenuItem_Click(object sender, EventArgs e) { Clipboard.Clear(); Clipboard.SetText(GetTcl()); } /// /// Export the grid. This is public mostly for debugging. /// /// public string GetGridAsTcl() { Clipboard.Clear(); StringBuilder sb = new StringBuilder(); foreach (DataGridViewColumn col in GetColumnsInOrder()) { if (sb.Length > 0) sb.Append("\r\n"); sb.Append('{'); int index = 0; int count = 1; // Minimim of 1. If the top row is blank, don't remove it. foreach (DataGridViewRow row in dataGridView1.Rows) { index++; DataGridViewCell cell = row.Cells[col.Index]; if ((cell.Value != null) && (((string)cell.Value) != "")) count = index; } //foreach (DataGridViewRow row in dataGridView1.Rows) for (index = 0; index < count; index++) { DataGridViewCell cell = dataGridView1.Rows[index].Cells[col.Index]; if (index >= 1) // This is a formula. sb.Append("\r\n "); sb.Append(TclList.TclQuote((cell.Value ?? "").ToString())); if (index == 0) // About to start the formulas. (There may be none, but this is where they would be. sb.Append("\r\n {"); } sb.Append("\r\n }\r\n}"); } return sb.ToString(); } private void copyFormulasAsTCLToolStripMenuItem_Click(object sender, EventArgs e) { Clipboard.SetText(GetGridAsTcl()); } private class ParsedColumn { public String Name { get; private set; } public IList Formulas { get; private set; } public ParsedColumn(string toParse) { IList pieces = TclList.Parse(toParse); if (null == pieces) throw new ArgumentException("not a valid list: " + toParse); if (pieces.Count != 2) throw new ArgumentException("list should have two items, name followed by formulas: " + toParse); Name = pieces[0]; Formulas = TclList.Parse(pieces[1]); if (null == Formulas) throw new ArgumentException("not a valid list: " + pieces[1]); } public static IList GetAll(string toParse) { IList pieces = TclList.Parse(toParse); if (null == pieces) throw new ArgumentException("not a valid list: " + toParse); IList result = new List(pieces.Count); foreach (String columnDescription in pieces) result.Add(new ParsedColumn(columnDescription)); return result; } public static bool IsValidGrid(string toParse) { try { GetAll(toParse); return true; } catch (ArgumentException) { return false; } } } private void pasteFormlasFromTCLToolStripMenuItem_Click(object sender, EventArgs e) { try { string toPaste = Clipboard.GetText(); IList columns = ParsedColumn.GetAll(toPaste); int columnCount = Math.Max(columns.Count, 1); // Minimum of one column. int rowCount = 2; // Minimum of 2 rows. foreach (ParsedColumn column in columns) rowCount = Math.Max(rowCount, 1 + column.Formulas.Count); dataGridView1.ColumnCount = columnCount; dataGridView1.RowCount = rowCount; for (int column = 0; column < columnCount; column++) { ParsedColumn parsedColumn = columns[column]; dataGridView1.Rows[0].Cells[column].Value = parsedColumn.Name; for (int row = 1; row < rowCount; row++) { int formulaIndex = row - 1; // The first row is the name. if (formulaIndex < parsedColumn.Formulas.Count) dataGridView1.Rows[row].Cells[column].Value = parsedColumn.Formulas[formulaIndex]; else dataGridView1.Rows[row].Cells[column].Value = ""; } } UpdateCellEditor(); } catch { // This shouldn't happen because we disable the menu item if the input is no good. } } } }