using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Xml; using TradeIdeas.Extensions; using TradeIdeas.ServerConnection; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.TIProGUI; using TradeIdeas.XML; namespace TIProDevExtension { public partial class ChartThemeGenerator : Form { //private readonly string _label1HelpText = "This tool generates an individual TI Chart Theme XML that can be added to the Global Settings (or your Custom4.xml).\r\n"; //private readonly string _label2HelpText = "1. Main Chart Style - Required - Start by loading a Chart and changing the Chart Styles (Background Color, Up Down Colors, etc), then click \"Save or Share to Cloud\", and copy the Cloud Link. Paste that Cloud Link into the \"Main Chart Cloud Link\" textbox below.\r\n"; //private readonly string _label3HelpText = "2. PIP Chart Style - Not Required - if you want the PIP chart to have a different style than the Main Chart, open a chart and set the main Chart Styles ((Background Color, Up Down Colors, etc) to what you would like the PIP Chart to look like. Click \"Save or Share to Cloud\", and copy the Cloud Link. Paste that Cloud Link into the \"PIP Chart Cloud Link\" textbox below."; private IConnectionMaster _connectionMaster; private XmlElement _mainStyleDoc; private XmlElement _pipStyleDoc; private XmlDocument _mergedStyleDoc; public ChartThemeGenerator(IConnectionMaster connectionMaster) { _connectionMaster = connectionMaster; InitializeComponent(); Icon = GuiEnvironment.DefaultIcon.Icon; StartPosition = FormStartPosition.CenterScreen; lblHelp_1.Text = "This tool generates an individual TI Chart Theme XML that can be added to the Global Settings (or your Custom4.xml).\r\n"; lblHelp_2.Text = "1. Main Chart Style - Required - Start by loading a Chart and changing the Chart Styles (Background Color, Up Down Colors, etc), then click \"Save or Share to Cloud\", and copy the Cloud Link. Paste that Cloud Link into the \"Main Chart Cloud Link\" textbox below.\r\n"; lblHelp_3.Text = "2. PIP Chart Style - Not Required - if you want the PIP chart to have a different style than the Main Chart, open a chart and set the main Chart Styles ((Background Color, Up Down Colors, etc) to what you would like the PIP Chart to look like. Click \"Save or Share to Cloud\", and copy the Cloud Link. Paste that Cloud Link into the \"PIP Chart Cloud Link\" textbox below."; var themeType = new Dictionary { { "TI Theme", "" }, { "TI Dark Theme", ChartThemesHelper.TIDarkThemeType }, { "TI Light Theme", ChartThemesHelper.TILightThemeType } }; var themeTypeKeyValue = themeType.ToArray(); comboBoxThemeType.DisplayMember = "Value"; comboBoxThemeType.ValueMember = "Key"; comboBoxThemeType.DataSource = themeTypeKeyValue; comboBoxSSWBaseColor.SelectedIndex = 0; this.txtBoxName.Focus(); this.txtBoxName.Select(); } private void comboBoxThemeType_DrawWhiteBackgroundItem(object sender, DrawItemEventArgs e) { if (e.Index < 0) { return; } Brush myBrush = Brushes.Black; ComboBox control = (ComboBox)sender; var item = (KeyValuePair)control.Items[e.Index]; e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds); e.Graphics.DrawString(item.Key.ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault); e.DrawFocusRectangle(); } private void comboBoxSSWBackground_DrawWhiteBackgroundItem(object sender, DrawItemEventArgs e) { if (e.Index < 0) { return; } Brush myBrush = Brushes.Black; ComboBox control = (ComboBox)sender; var item = (string)control.Items[e.Index]; e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds); e.Graphics.DrawString(item, e.Font, myBrush, e.Bounds, StringFormat.GenericDefault); e.DrawFocusRectangle(); } private void btnGenerate_Click(object sender, EventArgs e) { _mainStyleDoc = null; _pipStyleDoc = null; if (!txtBoxName.Text.HasValue()) { txtBoxName.Focus(); return; } if (!txtBoxMainStyle.Text.HasValue()) { txtBoxMainStyle.Focus(); return; } var mainStyleLayoutCode = GetLayoutCodeFromText(txtBoxMainStyle.Text); if (mainStyleLayoutCode.HasValue()) { var command = TalkWithServer.CreateMessage("command", "cloud_import", "code", mainStyleLayoutCode); SendLoadMainStyleImportCommand(command); } var pipStyleLayoutCode = GetLayoutCodeFromText(txtBoxPIPStyle.Text); if (pipStyleLayoutCode.HasValue()) { var command = TalkWithServer.CreateMessage("command", "cloud_import", "code", pipStyleLayoutCode); SendLoadPIPStyleImportCommand(command); } } private void btnPreview_Click(object sender, EventArgs e) { this.OnPreviewClicked(); } private void SendLoadMainStyleImportCommand(object command) { _connectionMaster.SendManager.SendMessage((Dictionary)command, LoadImportMainStyleCommandResponse, false, command); } private void LoadImportMainStyleCommandResponse(byte[] body, object clientId) { if (null == body) SendLoadMainStyleImportCommand(clientId); else this.BeginInvokeIfRequired(delegate { LoadImportMainStyleCommandResponse(body); }); } private void LoadImportMainStyleCommandResponse(byte[] body) { XmlNode wrapper = XmlHelper.Get(body).Node(0).Node("LAYOUT"); bool clearPrevious = wrapper.Property("CLEAR_PREVIOUS", false); bool fillScreen = wrapper.Property("FILL_SCREEN", false); /* Bug / TODO: * Currently fill screen works pretty well if you are making things bigger. * If you a trying to move things to a smaller screen you have problems. * First we do a normal load layout, then we adjust the layout. But the normal * load layout will see that some windows are off your screen, and will adjust * them before ScreenFiller gets a chance to look at them. Maybe we need a * special flag to tell the normal layout manager not to fix those windows that * are off screen. */ XmlDocument layout = XmlHelper.Get(Encoding.UTF8.GetBytes(wrapper.Text())); if (null == layout) { StringBuilder sb = new StringBuilder("Unable to load this item."); if (wrapper.Text() == "") // This isn't necessarily right. // When a layout is too big it will also appear this way. // The server knows the difference, but it doesn't tell the client. sb.Append(" (Not found.)"); else sb.Append(" (Corrupted.)"); MessageBox.Show(sb.ToString(), "Load from Cloud Failure"); } else { _mainStyleDoc = layout.DocumentElement; this.btnPreview.Enabled = true; this.OnLayoutLoaded(); } } private void SendLoadPIPStyleImportCommand(object command) { _connectionMaster.SendManager.SendMessage((Dictionary)command, LoadImportPIPStyleCommandResponse, false, command); } private void LoadImportPIPStyleCommandResponse(byte[] body, object clientId) { if (null == body) SendLoadPIPStyleImportCommand(clientId); else this.BeginInvokeIfRequired(delegate { LoadImportPIPStyleCommandResponse(body); }); } private void LoadImportPIPStyleCommandResponse(byte[] body) { XmlNode wrapper = XmlHelper.Get(body).Node(0).Node("LAYOUT"); bool clearPrevious = wrapper.Property("CLEAR_PREVIOUS", false); bool fillScreen = wrapper.Property("FILL_SCREEN", false); /* Bug / TODO: * Currently fill screen works pretty well if you are making things bigger. * If you a trying to move things to a smaller screen you have problems. * First we do a normal load layout, then we adjust the layout. But the normal * load layout will see that some windows are off your screen, and will adjust * them before ScreenFiller gets a chance to look at them. Maybe we need a * special flag to tell the normal layout manager not to fix those windows that * are off screen. */ XmlDocument layout = XmlHelper.Get(Encoding.UTF8.GetBytes(wrapper.Text())); if (null == layout) { StringBuilder sb = new StringBuilder("Unable to load this item."); if (wrapper.Text() == "") // This isn't necessarily right. // When a layout is too big it will also appear this way. // The server knows the difference, but it doesn't tell the client. sb.Append(" (Not found.)"); else sb.Append(" (Corrupted.)"); MessageBox.Show(sb.ToString(), "Load from Cloud Failure"); } else { _pipStyleDoc = layout.DocumentElement; this.OnLayoutLoaded(); } } private void OnLayoutLoaded() { if (_mainStyleDoc == null || (txtBoxPIPStyle.Text.HasValue() && _pipStyleDoc == null)) { return; } XmlDocument result = new XmlDocument(); XmlNode container = result.CreateElement("LAYOUT"); result.AppendChild(container); XmlNode mainChartNode = null; XmlNode pipChartNode = null; if (_mainStyleDoc.ChildNodes.Count > 0) { foreach (XmlNode node in _mainStyleDoc.ChildNodes) { string formType = node.Property("FORM_TYPE"); if (formType.EqualsIgnoreCase("CHART")) { mainChartNode = node.CloneNode(true); break; } } } if (_pipStyleDoc != null && _pipStyleDoc.ChildNodes.Count > 0) { foreach (XmlNode node in _pipStyleDoc.ChildNodes) { string formType = node.Property("FORM_TYPE"); if (formType.EqualsIgnoreCase("CHART")) { pipChartNode = node.CloneNode(true); break; } } } LayoutChartStyleModel mainChartLayoutStyles = null; LayoutChartStyleModel pipChartLayoutStyles = null; if (mainChartNode != null) { mainChartNode.SetProperty("SelectedChartThemeIndex", string.Empty); mainChartNode.SetProperty("ChartThemeGeneratorPreview", true); mainChartLayoutStyles = ChartThemesMapper.XmlNodeToLayoutChartStyleModel(mainChartNode); mainChartLayoutStyles.SingleStockBaseColor = (string)this.comboBoxSSWBaseColor.SelectedItem; mainChartNode.SetProperty("SingleStockBaseColor", mainChartLayoutStyles.SingleStockBaseColor); if (pipChartNode != null) { mainChartNode.SetProperty("ChartThemeGeneratorPreviewHasPIPStyle", true); pipChartLayoutStyles = ChartThemesMapper.XmlNodeToLayoutChartStyleModel(pipChartNode); mainChartNode.SetProperty("PreviewPIPSelectedChartBackgroundColor", pipChartLayoutStyles.SelectedChartBackgroundColor); mainChartNode.SetProperty("PreviewPIPSelectedChartGradientColor", pipChartLayoutStyles.SelectedChartGradientColor); mainChartNode.SetProperty("PreviewPIPSelectedPriceTextColor", pipChartLayoutStyles.SelectedPriceTextColor); mainChartNode.SetProperty("PreviewPIPSelectedDateTextColor", pipChartLayoutStyles.SelectedDateTextColor); mainChartNode.SetProperty("PreviewPIPSelectedUpCandleColor", pipChartLayoutStyles.SelectedUpCandleColor); mainChartNode.SetProperty("PreviewPIPSelectedDownCandleColor", pipChartLayoutStyles.SelectedDownCandleColor); mainChartNode.SetProperty("PreviewPIPSelectedOutlineWickColor", pipChartLayoutStyles.SelectedOutlineWickColor); mainChartNode.SetProperty("PreviewPIPSelectedStatusLineTextColor", pipChartLayoutStyles.SelectedStatusLineTextColor); mainChartNode.SetProperty("PreviewPIPSelectedLineColor", pipChartLayoutStyles.SelectedLineColor); mainChartNode.SetProperty("PreviewPIPWatermarkOpacity", pipChartLayoutStyles.WatermarkOpacity); mainChartNode.SetProperty("PreviewPIPIsWatermarkDarkTheme", pipChartLayoutStyles.IsWatermarkDarkTheme); mainChartNode.SetProperty("PreviewPIPColorPickerIsGradientAreaColorChecked", pipChartLayoutStyles.ColorPickerIsGradientAreaColorChecked); mainChartNode.SetProperty("PreviewPIPColorPickerIsGradientChecked", pipChartLayoutStyles.ColorPickerIsGradientChecked); } XmlNode copiedNode = result.ImportNode(mainChartNode, true); container.AppendChild(copiedNode); } _mergedStyleDoc = result; var chartThemeModel = new ChartThemeModel { MainChartStyleLayout = mainChartLayoutStyles, PipChartStyleLayout = pipChartLayoutStyles }; var themeType = ((KeyValuePair)comboBoxThemeType.SelectedItem).Value; var serializeText = ChartThemesHelper.SerializeChartThemeModel(chartThemeModel); txtOutput.Text = $"" + Environment.NewLine + serializeText + Environment.NewLine + $""; } private void OnPreviewClicked() { if (_mergedStyleDoc == null) { return; } LayoutManager.Instance().Restore(_mergedStyleDoc.DocumentElement, false); } private static string GetLayoutCodeFromText(string rawCloudLink) { string scrubbedLink = rawCloudLink.Trim(); System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("code=([^=^&]+)&?"); var match = regex.Match(scrubbedLink); if (match.Success) return "https://www.trade-ideas.com/Cloud.html?code=" + match.Groups[1].Value; return scrubbedLink; } } /// /// The standard way to add an extension to the menu. /// class ChartThemeGeneratorExtension : IMainInit { /// /// Normal init for extensions. /// /// /// public static IList GetExtensions(Object mainProgram) { List result = new List(1); result.Add(new ChartThemeGeneratorExtension((IMainProgram)mainProgram)); return result; } private readonly IMainProgram _mainProgram; private ChartThemeGeneratorExtension(IMainProgram mainProgram) { _mainProgram = mainProgram; } public void MainInit() { _mainProgram.AddToToolsMenu("Chart Theme Generator", delegate { new ChartThemeGenerator(_mainProgram.ConnectionMaster).Show(); }); } } }