#region ================== Namespaces using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using CodeImp.DoomBuilder.Rendering; using ScintillaNET; #endregion namespace CodeImp.DoomBuilder.Controls { internal partial class ScriptEditorPreviewControl : UserControl { #region ================== Constants private const int HIGHLIGHT_INDICATOR = 8; #endregion #region ================== Variables private Dictionary styletranslation; private string highlightedword; private Color indicatorcolor; private int lastcaretpos; private readonly HashSet bracechars; #endregion #region ================== Properties public string FontName { set { ApplyFont(value); } } public int FontSize { set { ApplyFontSize(value); } } public bool FontBold { set { ApplyFontBold(value); } } public int TabWidth { set { scriptedit.TabWidth = value; } } public bool ShowLineNumbers { set { UpdateLineNumbers(value); } } public bool ShowFolding { set { UpdateFolding(value); } } // Colors public Color ScriptBackground { set { scriptedit.Styles[Style.Default].BackColor = value; scriptedit.CaretForeColor = PixelColor.FromColor(value).Inverse().ToColor(); scriptedit.Styles[Style.LineNumber].BackColor = value; scriptedit.SetWhitespaceBackColor(true, value); foreach(KeyValuePair group in styletranslation) scriptedit.Styles[group.Key].BackColor = value; } } public Color FoldForeColor { set { for(int i = 25; i < 32; i++) scriptedit.Markers[i].SetBackColor(value); } } public Color FoldBackColor { set { scriptedit.SetFoldMarginColor(true, value); scriptedit.SetFoldMarginHighlightColor(true, value); for(int i = 25; i < 32; i++) scriptedit.Markers[i].SetForeColor(value); } } public Color LineNumbers { set { ApplyStyleColor(ScriptStyleType.LineNumber, value); } } public Color PlainText { set { ApplyStyleColor(ScriptStyleType.PlainText, value); } } public Color Comments { set { ApplyStyleColor(ScriptStyleType.Comment, value); } } public Color Keywords { set { ApplyStyleColor(ScriptStyleType.Keyword, value); } } public Color Properties { set { ApplyStyleColor(ScriptStyleType.Property, value); } } public Color Literals { set { ApplyStyleColor(ScriptStyleType.Literal, value); } } public Color Constants { set { ApplyStyleColor(ScriptStyleType.Constant, value); } } public Color Strings { set { ApplyStyleColor(ScriptStyleType.String, value); } } public Color Includes { set { ApplyStyleColor(ScriptStyleType.Include, value); } } public Color SelectionForeColor { set { scriptedit.SetSelectionForeColor(true, value); } } public Color SelectionBackColor { set { scriptedit.SetSelectionBackColor(true, value); } } public Color WhitespaceColor { set { scriptedit.SetWhitespaceForeColor(true, value); scriptedit.SetWhitespaceBackColor(false, value); // Otherwise selection back color won't be applied correctly... } } public Color BraceHighlight { set { scriptedit.Styles[Style.BraceLight].BackColor = value; } } public Color BadBraceHighlight { set { scriptedit.Styles[Style.BraceBad].BackColor = value; } } public Color ScriptIndicator { set { indicatorcolor = value; UpdateWordHighlight(); } } #endregion #region ================== Constructor / Setup public ScriptEditorPreviewControl() { InitializeComponent(); if(LicenseManager.UsageMode != LicenseUsageMode.Designtime) { bracechars = new HashSet { '{', '}', '(', ')', '[', ']' }; SetupStyles(); } } // This sets up the script editor with default settings private void SetupStyles() { // Symbol margin scriptedit.Margins[0].Type = MarginType.Symbol; scriptedit.Margins[0].Width = 20; scriptedit.Margins[0].Mask = 0; // No markers here scriptedit.Margins[0].Cursor = MarginCursor.Arrow; scriptedit.Margins[0].Sensitive = true; // Line numbers margin if(General.Settings.ScriptShowLineNumbers) { scriptedit.Margins[1].Type = MarginType.Number; scriptedit.Margins[1].Width = 16; } scriptedit.Margins[1].Mask = 0; // No markers here // Spacing margin scriptedit.Margins[2].Type = MarginType.Symbol; scriptedit.Margins[2].Width = 5; scriptedit.Margins[2].Cursor = MarginCursor.Arrow; scriptedit.Margins[2].Mask = 0; // No markers here // Set lexer scriptedit.Lexer = Lexer.CppNoCase; // Set script editor preview text scriptedit.Text = "#include \"zcommon.acs\"" + "\n" + "script 667 ENTER //A simple script example" + "\n" + "{" + "\n" + " int red = CR_RED;" + "\n" + "\tPrint(s:\"Welcome!\");" + "\n" + "\tThing_ChangeTID(0, 667);" + "\n" + "} } // <- A spare brace!"; // No text editing beyond this point! scriptedit.ReadOnly = true; // Set keywords (0) const string keywords = "Print HudMessageBold Thing_ChangeTID"; scriptedit.SetKeywords(0, keywords.ToLowerInvariant()); // Set constants (1) const string constants = "CR_RED"; scriptedit.SetKeywords(1, constants.ToLowerInvariant()); // Set properties (3) const string properties = "script enter int"; scriptedit.SetKeywords(3, properties.ToLowerInvariant()); // Reset document slyle scriptedit.ClearDocumentStyle(); scriptedit.StyleResetDefault(); // Create style translation dictionary styletranslation = new Dictionary { { 0, ScriptStyleType.PlainText }, // End of line { 10, ScriptStyleType.PlainText }, // Operator { 11, ScriptStyleType.PlainText }, // Identifier { 33, ScriptStyleType.LineNumber }, { 1, ScriptStyleType.Comment }, { 2, ScriptStyleType.Comment }, { 5, ScriptStyleType.Keyword }, { 4, ScriptStyleType.Literal }, { 7, ScriptStyleType.Literal }, { 16, ScriptStyleType.Constant }, { 37, ScriptStyleType.LineNumber }, { 6, ScriptStyleType.String }, { 9, ScriptStyleType.Include }, { 19, ScriptStyleType.Property }, }; // Set the default style and settings scriptedit.Styles[Style.Default].Font = General.Settings.ScriptFontName; scriptedit.Styles[Style.Default].Size = General.Settings.ScriptFontSize; scriptedit.Styles[Style.Default].Bold = General.Settings.ScriptFontBold; scriptedit.Styles[Style.Default].Italic = false; scriptedit.Styles[Style.Default].Underline = false; scriptedit.Styles[Style.Default].Case = StyleCase.Mixed; scriptedit.Styles[Style.Default].ForeColor = General.Colors.PlainText.ToColor(); scriptedit.Styles[Style.Default].BackColor = General.Colors.ScriptBackground.ToColor(); scriptedit.CaretPeriod = SystemInformation.CaretBlinkTime; scriptedit.CaretForeColor = General.Colors.ScriptBackground.Inverse().ToColor(); // Set tabulation settings scriptedit.UseTabs = General.Settings.ScriptUseTabs; scriptedit.TabWidth = General.Settings.ScriptTabWidth; // This applies the default style to all styles scriptedit.StyleClearAll(); // Set style for linenumbers and margins scriptedit.Styles[Style.LineNumber].BackColor = General.Colors.ScriptBackground.ToColor(); scriptedit.SetFoldMarginColor(true, General.Colors.ScriptFoldBackColor.ToColor()); scriptedit.SetFoldMarginHighlightColor(true, General.Colors.ScriptFoldBackColor.ToColor()); for(int i = 25; i < 32; i++) { scriptedit.Markers[i].SetForeColor(General.Colors.ScriptFoldBackColor.ToColor()); scriptedit.Markers[i].SetBackColor(General.Colors.ScriptFoldForeColor.ToColor()); } // Set style for (mis)matching braces scriptedit.Styles[Style.BraceLight].BackColor = General.Colors.ScriptBraceHighlight.ToColor(); scriptedit.Styles[Style.BraceBad].BackColor = General.Colors.ScriptBadBraceHighlight.ToColor(); // Set whitespace color scriptedit.SetWhitespaceForeColor(true, General.Colors.ScriptWhitespace.ToColor()); // Set selection colors scriptedit.SetSelectionForeColor(true, General.Colors.ScriptSelectionForeColor.ToColor()); scriptedit.SetSelectionBackColor(true, General.Colors.ScriptSelectionBackColor.ToColor()); indicatorcolor = General.Colors.ScriptIndicator.ToColor(); // Set words colors foreach(KeyValuePair group in styletranslation) { // Apply color to style int colorindex; switch(group.Value) { case ScriptStyleType.PlainText: colorindex = ColorCollection.PLAINTEXT; break; case ScriptStyleType.Comment: colorindex = ColorCollection.COMMENTS; break; case ScriptStyleType.Constant: colorindex = ColorCollection.CONSTANTS; break; case ScriptStyleType.Keyword: colorindex = ColorCollection.KEYWORDS; break; case ScriptStyleType.LineNumber: colorindex = ColorCollection.LINENUMBERS; break; case ScriptStyleType.Literal: colorindex = ColorCollection.LITERALS; break; case ScriptStyleType.String: colorindex = ColorCollection.STRINGS; break; case ScriptStyleType.Include: colorindex = ColorCollection.INCLUDES; break; case ScriptStyleType.Property: colorindex = ColorCollection.PROPERTIES; break; default: colorindex = ColorCollection.PLAINTEXT; break; } scriptedit.Styles[group.Key].ForeColor = General.Colors.Colors[colorindex].ToColor(); scriptedit.Styles[group.Key].BackColor = General.Colors.ScriptBackground.ToColor(); } // Setup folding UpdateFolding(General.Settings.ScriptShowFolding); // Rearrange the layout this.PerformLayout(); } #endregion #region ================== Methods private void ApplyStyleColor(ScriptStyleType type, Color color) { foreach(KeyValuePair group in styletranslation) if(group.Value == type) scriptedit.Styles[group.Key].ForeColor = color; } private void ApplyFont(string font) { foreach(KeyValuePair group in styletranslation) scriptedit.Styles[group.Key].Font = font; } private void ApplyFontBold(bool bold) { foreach(KeyValuePair group in styletranslation) scriptedit.Styles[group.Key].Bold = bold; } private void ApplyFontSize(int size) { foreach(KeyValuePair group in styletranslation) scriptedit.Styles[group.Key].Size = size; } private void UpdateLineNumbers(bool show) { if(show) { scriptedit.Margins[1].Type = MarginType.Number; scriptedit.Margins[1].Width = 16; } else { scriptedit.Margins[1].Type = MarginType.Symbol; scriptedit.Margins[1].Width = 0; } } private void UpdateFolding(bool show) { if(show) { // Instruct the lexer to calculate folding scriptedit.SetProperty("fold", "1"); scriptedit.SetProperty("fold.compact", "0"); // 1 = folds blank lines scriptedit.SetProperty("fold.comment", "1"); // Enable block comment folding scriptedit.SetProperty("fold.preprocessor", "1"); // Enable #region folding scriptedit.SetFoldFlags(FoldFlags.LineAfterContracted); // Draw line below if not expanded // Configure a margin to display folding symbols scriptedit.Margins[2].Type = MarginType.Symbol; scriptedit.Margins[2].Mask = Marker.MaskFolders; scriptedit.Margins[2].Sensitive = true; scriptedit.Margins[2].Width = 12; // Configure folding markers with respective symbols scriptedit.Markers[Marker.Folder].Symbol = MarkerSymbol.BoxPlus; scriptedit.Markers[Marker.FolderOpen].Symbol = MarkerSymbol.BoxMinus; scriptedit.Markers[Marker.FolderEnd].Symbol = MarkerSymbol.BoxPlusConnected; scriptedit.Markers[Marker.FolderMidTail].Symbol = MarkerSymbol.TCorner; scriptedit.Markers[Marker.FolderOpenMid].Symbol = MarkerSymbol.BoxMinusConnected; scriptedit.Markers[Marker.FolderSub].Symbol = MarkerSymbol.VLine; scriptedit.Markers[Marker.FolderTail].Symbol = MarkerSymbol.LCorner; // Enable automatic folding scriptedit.AutomaticFold = (AutomaticFold.Show | AutomaticFold.Click | AutomaticFold.Change); } else { // Disable folding scriptedit.SetProperty("fold", "0"); scriptedit.SetProperty("fold.compact", "0"); scriptedit.Margins[2].Type = MarginType.Symbol; scriptedit.Margins[2].Mask = 0; // No markers here scriptedit.Margins[2].Sensitive = false; scriptedit.Margins[2].Width = 5; scriptedit.AutomaticFold = AutomaticFold.None; } } private void UpdateWordHighlight() { // If a word is selected, highlight the same words if(scriptedit.SelectedText != highlightedword) { // Highlight only when whole word is selected if(!string.IsNullOrEmpty(scriptedit.SelectedText) && scriptedit.GetWordFromPosition(scriptedit.SelectionStart) == scriptedit.SelectedText) { HighlightWord(scriptedit.SelectedText); } else { // Clear highlight scriptedit.IndicatorCurrent = HIGHLIGHT_INDICATOR; scriptedit.IndicatorClearRange(0, scriptedit.TextLength); } highlightedword = scriptedit.SelectedText; } } private void HighlightWord(string text) { // Remove all uses of our indicator scriptedit.IndicatorCurrent = HIGHLIGHT_INDICATOR; scriptedit.IndicatorClearRange(0, scriptedit.TextLength); // Update indicator appearance scriptedit.Indicators[HIGHLIGHT_INDICATOR].Style = IndicatorStyle.RoundBox; scriptedit.Indicators[HIGHLIGHT_INDICATOR].Under = true; scriptedit.Indicators[HIGHLIGHT_INDICATOR].ForeColor = indicatorcolor; scriptedit.Indicators[HIGHLIGHT_INDICATOR].OutlineAlpha = 50; scriptedit.Indicators[HIGHLIGHT_INDICATOR].Alpha = 30; // Search the document scriptedit.TargetStart = 0; scriptedit.TargetEnd = scriptedit.TextLength; scriptedit.SearchFlags = SearchFlags.WholeWord; while(scriptedit.SearchInTarget(text) != -1) { //mxd. Don't mark currently selected word if(scriptedit.SelectionStart != scriptedit.TargetStart && scriptedit.SelectionEnd != scriptedit.TargetEnd) { // Mark the search results with the current indicator scriptedit.IndicatorFillRange(scriptedit.TargetStart, scriptedit.TargetEnd - scriptedit.TargetStart); } // Search the remainder of the document scriptedit.TargetStart = scriptedit.TargetEnd; scriptedit.TargetEnd = scriptedit.TextLength; } } #endregion #region ================== Events private void scriptedit_UpdateUI(object sender, UpdateUIEventArgs e) { UpdateWordHighlight(); // Has the caret changed position? int caretpos = scriptedit.CurrentPosition; if(lastcaretpos != caretpos) { lastcaretpos = caretpos; int bracepos1 = -1; // Is there a brace to the left or right? if(caretpos > 0 && bracechars.Contains((char)scriptedit.GetCharAt(caretpos - 1))) bracepos1 = (caretpos - 1); else if(bracechars.Contains((char)(scriptedit.GetCharAt(caretpos)))) bracepos1 = caretpos; if(bracepos1 > -1) { // Find the matching brace int bracepos2 = scriptedit.BraceMatch(bracepos1); if(bracepos2 == Scintilla.InvalidPosition) scriptedit.BraceBadLight(bracepos1); else scriptedit.BraceHighlight(bracepos1, bracepos2); } else { // Turn off brace matching scriptedit.BraceHighlight(Scintilla.InvalidPosition, Scintilla.InvalidPosition); } } } #endregion } }