From 39814f19a5fbf5f9d773166e5ef233410cbbeb71 Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Sat, 4 Jul 2009 00:51:39 +0000 Subject: [PATCH] More fun with script editor git-svn-id: https://radegast.googlecode.com/svn/trunk@91 f7a694da-4d33-11de-9ad6-1127a62b9fcd --- Radegast/Core/LSLKeywordParser.cs | 4 +- Radegast/Core/Types/RRichTextBox.cs | 273 +++++++++++- Radegast/Core/Types/StringTokenizer.cs | 456 +++++++++++++++++++++ .../GUI/Consoles/Assets/ScriptEditor.Designer.cs | 58 +-- Radegast/GUI/Consoles/Assets/ScriptEditor.cs | 355 +++++----------- .../GUI/Consoles/Inventory/InventoryConsole.cs | 14 +- Radegast/Radegast.csproj | 5 + 7 files changed, 886 insertions(+), 279 deletions(-) create mode 100644 Radegast/Core/Types/StringTokenizer.cs diff --git a/Radegast/Core/LSLKeywordParser.cs b/Radegast/Core/LSLKeywordParser.cs index 4da007c..8fb5ebd 100644 --- a/Radegast/Core/LSLKeywordParser.cs +++ b/Radegast/Core/LSLKeywordParser.cs @@ -38,7 +38,7 @@ using OpenMetaverse; namespace Radegast { - struct LSLKeyWord + public struct LSLKeyWord { public string KeyWord; public string ToolTip; @@ -50,7 +50,7 @@ namespace Radegast } } - class LSLKeywordParser + public class LSLKeywordParser { private static Dictionary keyWords; diff --git a/Radegast/Core/Types/RRichTextBox.cs b/Radegast/Core/Types/RRichTextBox.cs index 11a1fac..2e6015a 100644 --- a/Radegast/Core/Types/RRichTextBox.cs +++ b/Radegast/Core/Types/RRichTextBox.cs @@ -33,6 +33,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; +using System.Drawing; +using System.Runtime.InteropServices; +using HWND = System.IntPtr; namespace Radegast { @@ -42,11 +45,26 @@ namespace Radegast private const short WM_PAINT = 0x00f; private bool _Paint = true; + private bool monoRuntime; + private System.Threading.Timer ttTimer; + private ToolTip ttKeyWords = new ToolTip(); public RRichTextBox() : base() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); + + // Are we running mono? + if (null == Type.GetType("Mono.Runtime")) + { + monoRuntime = false; + } + else + { + monoRuntime = true; + } + + ttTimer = new System.Threading.Timer(ttTimerElapsed, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); } protected override void WndProc(ref System.Windows.Forms.Message m) @@ -77,15 +95,137 @@ namespace Radegast public virtual void BeginUpdate() { _Paint = false; - SuspendLayout(); + if (!monoRuntime) + Win32.LockWindowUpdate(Handle); } public virtual void EndUpdate() { - ResumeLayout(); + if (!monoRuntime) + { + Win32.LockWindowUpdate((IntPtr)0); + Invalidate(); + } _Paint = true; } + #region Syntax highligting + private string rtfEscaped(string s) + { + return s.Replace(@"\", @"\\").Replace("{", @"\{").Replace("}", @"\}").Replace("\n", "\\par\n"); + } + + private List usedColors = new List(); + + private string colorTag(Color c, string s) + { + int index; + + if (usedColors.Contains(c)) + { + index = usedColors.IndexOf(c) + 1; + } + else + { + usedColors.Add(c); + index = usedColors.Count; + } + + return "\\cf" + index + " " + rtfEscaped(s) + "\\cf1 "; + } + + public Color CommentColor = Color.FromArgb(204, 76, 38); + public Color StringColor = Color.FromArgb(0, 51, 0); + public Dictionary KeyWords = LSLKeywordParser.KeyWords; + + protected override void OnTextChanged(EventArgs e) + { + if (monoRuntime) + { + // base.OnTextChanged(e); + // return; + } + + if (!_Paint) return; + + BeginUpdate(); + Win32.POINT scrollStatus = GetScrollPos(); + int selectionStart = SelectionStart; + int selectionLength = SelectionLength; + + StringTokenizer tokenizer = new StringTokenizer(Text.Replace("\t", " ")); + Token token; + StringBuilder body = new StringBuilder(); + usedColors.Clear(); + usedColors.Add(ForeColor); + + do + { + token = tokenizer.Next(); + + switch (token.Kind) + { + case TokenKind.Word: + if (KeyWords.ContainsKey(token.Value)) + { + body.Append(colorTag(KeyWords[token.Value].Color, token.Value)); + } + else + { + goto default; + } + break; + + case TokenKind.QuotedString: + body.Append(colorTag(StringColor, token.Value)); + break; + + case TokenKind.Comment: + body.Append(colorTag(CommentColor, token.Value)); + break; + + case TokenKind.EOL: + body.Append("\\par\n\\cf1 "); + break; + + default: + body.Append(rtfEscaped(token.Value)); + break; + } + + } while (token.Kind != TokenKind.EOF); + + StringBuilder colorTable = new StringBuilder(); + colorTable.Append(@"{\colortbl;"); + + foreach (Color color in usedColors) + { + colorTable.AppendFormat("\\red{0}\\green{1}\\blue{2};", color.R, color.G, color.B); + } + + colorTable.Append("}"); + + // Construct final rtf + StringBuilder rtf = new StringBuilder(); + rtf.AppendLine(@"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}}"); + //rtf.Append(@"\viewkind4\uc1\pard\lang1033\f0\fs20 "); + //rtf.AppendLine(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\f0\fnil\fcharset0 Courier New;}}"); + rtf.AppendLine(colorTable.ToString()); + rtf.Append(@"\pard\f0\fs18 "); + rtf.Append(body); + rtf.AppendLine(@"\par}"); + + // System.Console.WriteLine(rtf); + + // Restore scrollbars and cursor position + Rtf = rtf.ToString(); + SelectionStart = selectionStart; + SelectionLength = selectionLength; + SetScrollPos(scrollStatus); + EndUpdate(); + } + #endregion + public struct CursorLocation { public CursorLocation(int Line, int Column) @@ -151,5 +291,134 @@ namespace Radegast Select(Offset + value.Column, 0); } } + + #region ToolTips + private bool validWordChar(char c) + { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_'; + } + + private void ttTimerElapsed(Object sender) + { + if (InvokeRequired) + { + BeginInvoke(new MethodInvoker(delegate() { ttTimerElapsed(sender); })); + return; + } + + char trackedChar = GetCharFromPosition(trackedMousePos); + + if (!validWordChar(trackedChar)) + { + return; + } + + string trackedString = Text; + int trackedPos = GetCharIndexFromPosition(trackedMousePos); + int starPos; + int endPos; + + for (starPos = trackedPos; starPos >= 0 && validWordChar(trackedString[starPos]); starPos--) ; + for (endPos = trackedPos; endPos < trackedString.Length && validWordChar(trackedString[endPos]); endPos++) ; + string word = trackedString.Substring(starPos + 1, endPos - starPos - 1); + + if (!KeyWords.ContainsKey(word) || KeyWords[word].ToolTip == string.Empty) + { + return; + } + + ttKeyWords.Show(KeyWords[word].ToolTip, this, new Point(trackedMousePos.X, trackedMousePos.Y + 15), 120 * 1000); + } + + private Point trackedMousePos = new Point(0, 0); + + protected override void OnMouseMove(MouseEventArgs e) + { + Point currentMousePos = new Point(e.X, e.Y); + + if (currentMousePos != trackedMousePos) + { + trackedMousePos = currentMousePos; + ttTimer.Change(500, System.Threading.Timeout.Infinite); + ttKeyWords.Hide(this); + } + base.OnMouseMove(e); + } + #endregion + + #region Scrollbar positions functions + /// + /// Sends a win32 message to get the scrollbars' position. + /// + /// a POINT structre containing horizontal + /// and vertical scrollbar position. + private unsafe Win32.POINT GetScrollPos() + { + Win32.POINT res = new Win32.POINT(); + IntPtr ptr = new IntPtr(&res); + Win32.SendMessage(Handle, Win32.EM_GETSCROLLPOS, 0, ptr); + return res; + + } + + /// + /// Sends a win32 message to set scrollbars position. + /// + /// a POINT + /// conatining H/Vscrollbar scrollpos. + private unsafe void SetScrollPos(Win32.POINT point) + { + IntPtr ptr = new IntPtr(&point); + Win32.SendMessage(Handle, Win32.EM_SETSCROLLPOS, 0, ptr); + + } + + /// + /// Summary description for Win32. + /// + private class Win32 + { + private Win32() + { + } + + public const int WM_USER = 0x400; + public const int WM_PAINT = 0xF; + public const int WM_KEYDOWN = 0x100; + public const int WM_KEYUP = 0x101; + public const int WM_CHAR = 0x102; + + public const int EM_GETSCROLLPOS = (WM_USER + 221); + public const int EM_SETSCROLLPOS = (WM_USER + 222); + + public const int VK_CONTROL = 0x11; + public const int VK_UP = 0x26; + public const int VK_DOWN = 0x28; + public const int VK_NUMLOCK = 0x90; + + public const short KS_ON = 0x01; + public const short KS_KEYDOWN = 0x80; + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int x; + public int y; + } + + [DllImport("user32")] + public static extern int SendMessage(HWND hwnd, int wMsg, int wParam, IntPtr lParam); + [DllImport("user32")] + public static extern int PostMessage(HWND hwnd, int wMsg, int wParam, int lParam); + [DllImport("user32")] + public static extern short GetKeyState(int nVirtKey); + [DllImport("user32")] + public static extern int LockWindowUpdate(HWND hwnd); + } + #endregion } } \ No newline at end of file diff --git a/Radegast/Core/Types/StringTokenizer.cs b/Radegast/Core/Types/StringTokenizer.cs new file mode 100644 index 0000000..8575819 --- /dev/null +++ b/Radegast/Core/Types/StringTokenizer.cs @@ -0,0 +1,456 @@ +/********************************************************8 + * Author: Andrew Deren + * Date: July, 2004 + * http://www.adersoftware.com + * + * StringTokenizer class. You can use this class in any way you want + * as long as this header remains in this file. + * + **********************************************************/ +using System; +using System.IO; +using System.Text; + +namespace Radegast +{ + public enum TokenKind + { + Unknown, + Word, + Number, + QuotedString, + WhiteSpace, + Symbol, + Comment, + EOL, + EOF + } + + public class Token + { + int line; + int column; + string value; + TokenKind kind; + + public Token(TokenKind kind, string value, int line, int column) + { + this.kind = kind; + this.value = value; + this.line = line; + this.column = column; + } + + public int Column + { + get { return this.column; } + } + + public TokenKind Kind + { + get { return this.kind; } + } + + public int Line + { + get { return this.line; } + } + + public string Value + { + get { return this.value; } + } + } + + /// + /// StringTokenizer tokenized string (or stream) into tokens. + /// + public class StringTokenizer + { + const char EOF = (char)0; + + int line; + int column; + int pos; // position within data + + string data; + + bool ignoreWhiteSpace; + char[] symbolChars; + + int saveLine; + int saveCol; + int savePos; + + public StringTokenizer(TextReader reader) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + data = reader.ReadToEnd(); + + Reset(); + } + + public StringTokenizer(string data) + { + if (data == null) + throw new ArgumentNullException("data"); + + this.data = data; + + Reset(); + } + + /// + /// gets or sets which characters are part of TokenKind.Symbol + /// + public char[] SymbolChars + { + get { return this.symbolChars; } + set { this.symbolChars = value; } + } + + /// + /// if set to true, white space characters will be ignored, + /// but EOL and whitespace inside of string will still be tokenized + /// + public bool IgnoreWhiteSpace + { + get { return this.ignoreWhiteSpace; } + set { this.ignoreWhiteSpace = value; } + } + + private void Reset() + { + this.ignoreWhiteSpace = false; + this.symbolChars = new char[]{'=', '+', '-', '/', ',', '.', '*', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '{', '}', '[', ']', ':', ';', '<', '>', '?', '|', '\\'}; + + line = 1; + column = 1; + pos = 0; + } + + protected char LA(int count) + { + if (pos + count >= data.Length) + return EOF; + else + return data[pos+count]; + } + + protected char Consume() + { + char ret = data[pos]; + pos++; + column++; + + return ret; + } + + protected Token CreateToken(TokenKind kind, string value) + { + return new Token(kind, value, line, column); + } + + protected Token CreateToken(TokenKind kind) + { + string tokenData = data.Substring(savePos, pos-savePos); + return new Token(kind, tokenData, saveLine, saveCol); + } + + public Token Next() + { + ReadToken: + + char ch = LA(0); + switch (ch) + { + case EOF: + return CreateToken(TokenKind.EOF, string.Empty); + + case ' ': + case '\t': + { + if (this.ignoreWhiteSpace) + { + Consume(); + goto ReadToken; + } + else + return ReadWhitespace(); + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return ReadNumber(); + + case '\r': + { + StartRead(); + Consume(); + if (LA(0) == '\n') + Consume(); // on DOS/Windows we have \r\n for new line + + line++; + column=1; + + return CreateToken(TokenKind.EOL); + } + case '\n': + { + StartRead(); + Consume(); + line++; + column=1; + + return CreateToken(TokenKind.EOL); + } + + case '"': + { + return ReadString(); + } + + case '/': + { + if (LA(1) == '/') + { + return ReadComment(); + } + else if (LA(1) == '*') + { + return ReadStarComment(); + } + else + { + StartRead(); + Consume(); + return CreateToken(TokenKind.Symbol); + } + } + + default: + { + if (Char.IsLetter(ch) || ch == '_') + return ReadWord(); + else if (IsSymbol(ch)) + { + StartRead(); + Consume(); + return CreateToken(TokenKind.Symbol); + } + else + { + StartRead(); + Consume(); + return CreateToken(TokenKind.Unknown); + } + } + + } + } + + /// + /// save read point positions so that CreateToken can use those + /// + private void StartRead() + { + saveLine = line; + saveCol = column; + savePos = pos; + } + + /// + /// reads all whitespace characters (does not include newline) + /// + /// + protected Token ReadWhitespace() + { + StartRead(); + + Consume(); // consume the looked-ahead whitespace char + + while (true) + { + char ch = LA(0); + if (ch == '\t' || ch == ' ') + Consume(); + else + break; + } + + return CreateToken(TokenKind.WhiteSpace); + + } + + /// + /// reads number. Number is: DIGIT+ ("." DIGIT*)? + /// + /// + protected Token ReadNumber() + { + StartRead(); + + bool hadDot = false; + + Consume(); // read first digit + + while (true) + { + char ch = LA(0); + if (Char.IsDigit(ch)) + Consume(); + else if (ch == '.' && !hadDot) + { + hadDot = true; + Consume(); + } + else + break; + } + + return CreateToken(TokenKind.Number); + } + + /// + /// reads word. Word contains any alpha character or _ + /// + protected Token ReadWord() + { + StartRead(); + + Consume(); // consume first character of the word + + while (true) + { + char ch = LA(0); + if (Char.IsLetter(ch) || ch == '_') + Consume(); + else + break; + } + + return CreateToken(TokenKind.Word); + } + + /// + /// Reads he rest of line in // comment + /// + protected Token ReadComment() + { + StartRead(); + + Consume(); // consume first character of the comment + + while (true) + { + char ch = LA(0); + if (ch != EOF && ch != '\n' && ch != '\r') + Consume(); + else + break; + } + + return CreateToken(TokenKind.Comment); + } + + /// + /// Read c-style comments /* */ + /// + protected Token ReadStarComment() + { + StartRead(); + + Consume(); // consume first character of the comment + + while (true) + { + char ch = LA(0); + if (ch == EOF) + { + break; + } + else if (ch == '*' && LA(1) == '/') + { + Consume(); + Consume(); + break; + } + else + { + Consume(); + } + } + + return CreateToken(TokenKind.Comment); + } + + /// + /// reads all characters until next " is found. + /// If "" (2 quotes) are found, then they are consumed as + /// part of the string + /// + /// + protected Token ReadString() + { + StartRead(); + + Consume(); // read " + + while (true) + { + char ch = LA(0); + if (ch == EOF) + break; + else if (ch == '\r') // handle CR in strings + { + Consume(); + if (LA(0) == '\n') // for DOS & windows + Consume(); + + line++; + column = 1; + } + else if (ch == '\n') // new line in quoted string + { + Consume(); + + line++; + column = 1; + } + else if (ch == '"') + { + Consume(); + if (LA(0) != '"') + break; // done reading, and this quotes does not have escape character + else + Consume(); // consume second ", because first was just an escape + } + else + Consume(); + } + + return CreateToken(TokenKind.QuotedString); + } + + /// + /// checks whether c is a symbol character. + /// + protected bool IsSymbol(char c) + { + for (int i=0; i keywords = LSLKeywordParser.KeyWords; private InventoryLSL script; private UUID requestID; private bool populating = true; - private List lineBuffer = new List(); - private Color commentColor = Color.FromArgb(204, 76, 38); private string scriptName; - private System.Threading.Timer ttTimer; + private string fileName; public ScriptEditor(RadegastInstance instance) : this(instance, null) @@ -68,8 +65,8 @@ namespace Radegast this.instance = instance; this.script = script; lblScripStatus.Text = string.Empty; - ttTimer = new System.Threading.Timer(ttTimerElapsed, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); - + Dock = DockStyle.Fill; + this.TabStop = false; // Callbacks client.Assets.OnAssetReceived += new AssetManager.AssetReceivedCallback(Assets_OnAssetReceived); @@ -104,197 +101,17 @@ namespace Radegast } asset.Decode(); - rtbCode.BeginUpdate(); rtbCode.Text = ((AssetScriptText)asset).Source; - MakeColorSyntaxForAllText(); - rtbCode.EndUpdate(); - lblScripStatus.Text = scriptName; - } - - #region Syntax highlighting - struct WordAndPosition - { - public string Word; - public int Position; - public int Length; - public override string ToString() - { - string s = "Word = " + Word + ", Position = " + Position + ", Length = " + Length + "\n"; - return s; - } - }; - - private int ParseLine(string s) - { - lineBuffer.Clear(); - Regex r = new Regex(@"\w+|[^A-Za-z0-9_ \f\t\v]", RegexOptions.IgnoreCase | RegexOptions.Compiled); - Match m; - - for (m = r.Match(s); m.Success; m = m.NextMatch()) - { - WordAndPosition w = new WordAndPosition(); - w.Word = m.Value; - w.Position = m.Index; - w.Length = m.Length; - lineBuffer.Add(w); - } - - return lineBuffer.Count(); - } - - private Color Lookup(string s) - { - if (keywords.ContainsKey(s)) - { - return keywords[s].Color; - } - else - { - return Color.Black; - } - } - - private bool TestComment(string s) - { - string testString = s.Trim(); - if ((testString.Length >= 2) && - (testString[0] == '/') && - (testString[1] == '/') - ) - return true; - - return false; - } - - private void MakeColorSyntaxForCurrentLine() - { - int CurrentSelectionStart = rtbCode.SelectionStart; - int CurrentSelectionLength = rtbCode.SelectionLength; - - // find start of line - int pos = CurrentSelectionStart; - while ((pos > 0) && (rtbCode.Text[pos - 1] != '\n')) - pos--; - - int pos2 = CurrentSelectionStart; - while ((pos2 < rtbCode.Text.Length) && - (rtbCode.Text[pos2] != '\n')) - pos2++; - - string s = rtbCode.Text.Substring(pos, pos2 - pos); - if (TestComment(s) == true) - { - rtbCode.Select(pos, pos2 - pos); - rtbCode.SelectionColor = commentColor; - } - else - { - string previousWord = ""; - int count = ParseLine(s); - for (int i = 0; i < count; i++) - { - WordAndPosition wp = lineBuffer[i]; - - // check for comment - if (wp.Word == "/" && previousWord == "/") - { - // color until end of line - int posCommentStart = wp.Position - 1; - int posCommentEnd = pos2; - while (wp.Word != "\n" && i < count) - { - wp = lineBuffer[i]; - i++; - } - - i--; - - posCommentEnd = pos2; - rtbCode.Select(posCommentStart + pos, posCommentEnd - (posCommentStart + pos)); - rtbCode.SelectionColor = this.commentColor; - - } - else - { - - Color c = Lookup(wp.Word); - rtbCode.Select(wp.Position + pos, wp.Length); - rtbCode.SelectionColor = c; - } - - previousWord = wp.Word; - - } - } - - if (CurrentSelectionStart >= 0) - rtbCode.Select(CurrentSelectionStart, CurrentSelectionLength); - } - - private void MakeColorSyntaxForAllText() - { - populating = true; - - string s = rtbCode.Text; - int CurrentSelectionStart = rtbCode.SelectionStart; - int CurrentSelectionLength = rtbCode.SelectionLength; - - int count = ParseLine(s); - string previousWord = ""; - for (int i = 0; i < count; i++) - { - WordAndPosition wp = lineBuffer[i]; - - // check for comment - if (wp.Word == "/" && previousWord == "/") - { - // color until end of line - int posCommentStart = wp.Position - 1; - int posCommentEnd = i; - while (wp.Word != "\n" && i < count) - { - wp = lineBuffer[i]; - i++; - } - - i--; - - posCommentEnd = wp.Position; - rtbCode.Select(posCommentStart, posCommentEnd - posCommentStart); - rtbCode.SelectionColor = this.commentColor; - - } - else - { - - Color c = Lookup(wp.Word); - rtbCode.Select(wp.Position, wp.Length); - rtbCode.SelectionColor = c; - } - - previousWord = wp.Word; - - // Console.WriteLine(wp.ToString()); - } - - if (CurrentSelectionStart >= 0) - rtbCode.Select(CurrentSelectionStart, CurrentSelectionLength); - - populating = false; - + SetTitle(); } - private void rtbCode_TextChanged(object sender, EventArgs e) + private void SetTitle() { - if (populating) + if (detached) { - return; + FindForm().Text = scriptName + " - " + Properties.Resources.ProgramName + " script editor"; } - rtbCode.BeginUpdate(); - MakeColorSyntaxForCurrentLine(); - rtbCode.EndUpdate(); } - #endregion #region Detach/Attach private Control originalParent; @@ -343,7 +160,8 @@ namespace Radegast originalParent = Parent; Parent = detachedForm; detachedForm.ClientSize = new Size(873, 580); - detachedForm.Text = scriptName + " - " + Properties.Resources.ProgramName + " script editor"; + SetTitle(); + detachedForm.ActiveControl = this; detachedForm.Show(); detachedForm.FormClosing += new FormClosingEventHandler(detachedForm_FormClosing); @@ -422,74 +240,32 @@ namespace Radegast } #endregion - #region ToolTips - private bool validWordChar(char c) + + #region File I/O + private void tbtbSaveToDisk_Click_1(object sender, EventArgs e) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '_'; - } - - private void ttTimerElapsed(Object sender) - { - if (InvokeRequired) - { - BeginInvoke(new MethodInvoker(delegate() { ttTimerElapsed(sender); })); - return; - } - - char trackedChar = rtbCode.GetCharFromPosition(trackedMousePos); - - if (!validWordChar(trackedChar)) - { - return; - } - - string trackedString = rtbCode.Text; - int trackedPos = rtbCode.GetCharIndexFromPosition(trackedMousePos); - int starPos; - int endPos; - - for (starPos = trackedPos; starPos >= 0 && validWordChar(trackedString[starPos]); starPos--) ; - for (endPos = trackedPos; endPos < trackedString.Length && validWordChar(trackedString[endPos]); endPos++) ; - string word = trackedString.Substring(starPos + 1, endPos - starPos - 1); - - if (!keywords.ContainsKey(word) || keywords[word].ToolTip == string.Empty) + if (string.IsNullOrEmpty(fileName)) { + tbtbSaveToDisk_Click(sender, e); return; } - - ttKeyWords.Show(keywords[word].ToolTip, rtbCode, new Point(trackedMousePos.X, trackedMousePos.Y + 15), 120 * 1000); + File.WriteAllText(fileName, rtbCode.Text); } - private Point trackedMousePos = new Point(0, 0); - - private void rtbCode_MouseMove(object sender, MouseEventArgs e) - { - Point currentMousePos = new Point(e.X, e.Y); - - if (currentMousePos != trackedMousePos) - { - trackedMousePos = currentMousePos; - ttTimer.Change(500, System.Threading.Timeout.Infinite); - ttKeyWords.Hide(rtbCode); - } - } - #endregion - private void tbtbSaveToDisk_Click(object sender, EventArgs e) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Save script"; - dlg.Filter = "LSL script file (*.lsl)|*.lsl"; + dlg.Filter = "LSL script file (*.lsl)|*.lsl|Plain text file (*.txt)|*.txt"; dlg.FileName = RadegastMisc.SafeFileName(scriptName); DialogResult res = dlg.ShowDialog(); if (res == DialogResult.OK) { - File.WriteAllText(dlg.FileName, rtbCode.Text); + fileName = dlg.FileName; + scriptName = Path.GetFileName(fileName); + SetTitle(); + File.WriteAllText(fileName, rtbCode.Text); } } @@ -498,19 +274,19 @@ namespace Radegast { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Open script"; - dlg.Filter = "LSL script file (*.lsl)|*.lsl"; + dlg.Filter = "LSL script files (*.lsl)|*.lsl|Plain text files (*.txt)|*.txt|All files (*.*)|*.*"; dlg.Multiselect = false; DialogResult res = dlg.ShowDialog(); if (res == DialogResult.OK) { - scriptName = dlg.FileName; - rtbCode.Text = File.ReadAllText(dlg.FileName); - rtbCode.BeginUpdate(); - MakeColorSyntaxForAllText(); - rtbCode.EndUpdate(); + fileName = dlg.FileName; + scriptName = Path.GetFileName(fileName); + SetTitle(); + rtbCode.Text = File.ReadAllText(fileName); } } + #endregion private void tbtnExit_Click(object sender, EventArgs e) { @@ -523,5 +299,84 @@ namespace Radegast lblLine.Text = string.Format("Ln {0}", c.Line + 1); lblCol.Text = string.Format("Col {0}", c.Column + 1); } + + private bool spaceOrTab(char c) + { + return c == ' ' || c == '\t'; + } + + private int lastPos(string s, char c) + { + s = s.TrimEnd(); + + if (s == string.Empty) + return -1; + + if (s[s.Length - 1] == c) + { + return s.LastIndexOf(c); + } + return -1; + } + + private void rtbCode_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Tab) + { + rtbCode.SelectedText = " "; + e.Handled = true; + } + else if (e.KeyCode == Keys.Enter) + { + if (rtbCode.Lines.Length > 0) + { + RRichTextBox.CursorLocation cl = rtbCode.CursorPosition; + string prevLine = rtbCode.Lines[cl.Line]; + string addIndent = "\n"; + int pos; + + for (int spaces = 0; + spaces < prevLine.Length && spaceOrTab(prevLine[spaces]); + addIndent += prevLine[spaces++]) ; + + if ((pos = lastPos(prevLine.Substring(0, cl.Column), '{')) != -1) + { + addIndent += " "; + } + + rtbCode.SelectedText = addIndent; + int eat = 0; + for (eat = 0; (eat + rtbCode.SelectionStart) < rtbCode.Text.Length && rtbCode.Text[rtbCode.SelectionStart + eat] == ' '; eat++) ; + rtbCode.SelectionLength = eat; + rtbCode.SelectedText = ""; + e.Handled = true; + } + } + } + + private void rtbCode_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '}' && rtbCode.Lines.Length > 0) + { + string li = rtbCode.Lines[rtbCode.GetLineFromCharIndex(rtbCode.SelectionStart)]; + + if (li.Trim() == "") + { + rtbCode.BeginUpdate(); + int toDelete = li.Length; + + if (toDelete > 4) + { + toDelete = 4; + } + + rtbCode.SelectionStart -= toDelete; + rtbCode.SelectionLength = toDelete; + rtbCode.SelectedText = "}"; + rtbCode.EndUpdate(); + e.Handled = true; + } + } + } } } diff --git a/Radegast/GUI/Consoles/Inventory/InventoryConsole.cs b/Radegast/GUI/Consoles/Inventory/InventoryConsole.cs index bffed46..366e46c 100644 --- a/Radegast/GUI/Consoles/Inventory/InventoryConsole.cs +++ b/Radegast/GUI/Consoles/Inventory/InventoryConsole.cs @@ -842,7 +842,7 @@ namespace Radegast } - //if (folder.PreferredType == AssetType.Unknown) + if (folder.PreferredType == AssetType.Unknown) { ctxInv.Items.Add(new ToolStripSeparator()); ctxItem = new ToolStripMenuItem("Delete", null, OnInvContextClick); @@ -859,6 +859,13 @@ namespace Radegast ToolStripMenuItem ctxItem; + if (item.InventoryType == InventoryType.LSL) + { + ctxItem = new ToolStripMenuItem("Edit script", null, OnInvContextClick); + ctxItem.Name = "edit_script"; + ctxInv.Items.Add(ctxItem); + } + ctxItem = new ToolStripMenuItem("Rename", null, OnInvContextClick); ctxItem.Name = "rename_item"; ctxInv.Items.Add(ctxItem); @@ -1033,6 +1040,11 @@ namespace Radegast AttachmentPoint pt = (AttachmentPoint)((ToolStripMenuItem)sender).Tag; client.Appearance.Attach(item, pt); break; + + case "edit_script": + ScriptEditor se = new ScriptEditor(instance, (InventoryLSL)item); + se.Detached = true; + return; } #endregion } diff --git a/Radegast/Radegast.csproj b/Radegast/Radegast.csproj index a7cc85b..1a4ffec 100644 --- a/Radegast/Radegast.csproj +++ b/Radegast/Radegast.csproj @@ -45,6 +45,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -53,6 +54,7 @@ TRACE prompt 4 + true true @@ -61,6 +63,7 @@ full x86 prompt + true ..\bin\Release\ @@ -69,6 +72,7 @@ pdbonly x86 prompt + true @@ -117,6 +121,7 @@ Component + Component -- 2.11.0